I am creating custom tabs using react and material UI. In these tabs, we don't have a fixed tab count, based on the data, the length of the tab might increase and decrease. So we planned to add scrollable functionality If the tabs count is not occupied in the given space.
But by default, the scroll is appearing even if we have only one data.
below is the code for it.
import { Key, useState } from "react";
import { styled } from "#mui/material/styles";
import Button from "#mui/material/Button";
import { ReactComponent as Plus } from "./plus.svg";
import React from "react";
const Tabs = styled("div")`
width: 100%;
overflow: hidden;
margin: 1em 0 2em;
`;
const TabContainer = styled("ul")(() => ({
padding: 0,
margin: 0,
display: "flex",
flex: "1 1 auto",
overflowX: "auto",
overflowY: "hidden",
"& li": {
"&:first-of-type": {
marginLeft: 0,
"#media (max-width: 991px)": {
marginLeft: 10
}
}
},
"#media (max-width: 400px)": {
display: "unset"
}
}));
const Nav = styled("nav")(() => ({
display: "flex",
"#media (max-width: 991px)": {
textAlign: "center"
}
}));
const Tab = styled("li")(({ theme }) => ({
border: `2px solid ${theme.palette.grey[900]}`,
borderBottom: "none",
margin: "0 10px",
display: "block",
float: "left",
position: "relative",
borderTopRightRadius: 5,
borderTopLeftRadius: 5,
backgroundColor: theme.palette.common.white,
"#media (max-width: 991px)": {
float: "unset",
textAlign: "center"
},
"&.tab-current": {
border: `2px solid ${theme.palette.primary.main}`,
borderBottom: "none",
zIndex: 100,
"&::before": {
content: '""',
position: "absolute",
height: "2px",
right: "100%",
bottom: 0,
width: "1000px",
background: theme.palette.primary.main
},
"&::after": {
content: '""',
position: "absolute",
height: "2px",
right: "100%",
left: "100%",
bottom: 0,
width: "4000px",
background: theme.palette.primary.main
},
"& span": {
color: theme.palette.primary.main
}
}
}));
const Span = styled("span")(({ theme }) => ({
color: theme.palette.grey[900],
display: "block",
fontSize: "24px",
lineHeight: 2.5,
padding: "0 14px",
cursor: "pointer",
fontWeight: 400,
overflow: "hidden",
maxWidth: "ch",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}));
const AddGoalCTA = styled("span")(({ theme }) => ({
color: theme.palette.grey[900],
display: "block",
fontSize: "24px",
lineHeight: 2.5,
padding: "0 24px",
cursor: "pointer",
fontWeight: 900,
overflow: "hidden",
whiteSpace: "nowrap"
}));
const ButtonContainer = styled("div")(() => ({
float: "right",
"#media (max-width: 991px)": {
display: "none"
},
"& .MuiButton-root": {
padding: "10px"
}
}));
const PlusIcon = styled("span")(() => ({
width: "24px",
color: "black"
}));
const tabsData = ["Save For College", "Retirement Saving", "Save For Bike"];
// const tabsData = ["Save For College", "Retirement Saving", "Save For Bike", "Legacy Saving", "Save For Poker", "Save For Money"]
const TabsComponent = ({ hideEditButton, showAddTab = true }: any) => {
const [toggleState, setToggleState] = useState(0);
const toggleTab = (index: any) => {
setToggleState(index);
};
return (
<>
<Tabs>
<Nav>
<TabContainer>
{tabsData?.map((value: string, index: Key | null | undefined) => (
<Tab
className={toggleState === index ? "tab-current" : ""}
onClick={() => toggleTab(index)}
key={index}
tabIndex={0}
role="tab"
>
<Span>{value}</Span>
</Tab>
))}
{showAddTab && (
<Tab
onClick={() => {}}
tabIndex={0}
role="tab"
onKeyPress={() => {}}
>
<AddGoalCTA>
<PlusIcon as={Plus} />
</AddGoalCTA>
</Tab>
)}
</TabContainer>
{!hideEditButton && (
<ButtonContainer>
<Button variant="contained" onClick={() => {}}>
Edit
</Button>
</ButtonContainer>
)}
</Nav>
</Tabs>
</>
);
};
export default TabsComponent;
Here you can find the working demo - https://codesandbox.io/s/mui-tabs-9sgt89?file=/tab.tsx:0-4092
Please help me to resolve this one.
I checked your code. Actually for the current tab &::after has fixed width width: "4000px", which is causing the issue. you can reduce it to 1000px or to your convenience.
Hope this helps!!
Thanks
Related
I'm using MUI v5 to build linear progress bar.I have a scenario where if value in progress bar is 100%,I need to show tick icon at the end.The width of the bar with/without tick icon should be same,meaning icon shouldn't be placed after the bar.It should be at the end of bar.I tried with stylings and able to place tick icon at the end.But I'm unable to show the icon clearly as the bar overlaps with tick icon.
<div style={{ display: "flex", flexDirection: "row", position: "relative", alignItems: "center" }}>
<LinearProgress
variant="determinate"
sx={{
width: "100%",
borderRadius: "4px"
}}
value={50}
/>
<CheckCircleIcon sx={{ color: "blue" }} style={{ position: "absolute", width: "20px", display: "flex", justifyContent: "flex-end", right: "-2px", color: "#fff", fontWeight: "bold" }} />
</div>
Current Design
Expected Design
Here is a live demo where I've customized an MUI Slider with a checkmark SliderThumb.
The demo includes the foundation for using this as a progress bar:
Disable the slider to ignore user input. Keep in mind that disabling will change the color to gray. You can override disabled behavior through .Mui-disabled
Set the slider's value using a state variable that corresponds to your current progress
You may also choose to customize a LinearProgress component in the same way I've customized the Slider above. See the docs for LinearProgress customization.
Full slider code:
import * as React from 'react'
import Slider, { SliderThumb } from '#mui/material/Slider'
import { styled } from '#mui/material/styles'
import Box from '#mui/material/Box'
import CheckCircleIcon from '#mui/icons-material/CheckCircle'
const CheckMarkSlider = styled(Slider)(({ theme }) =>
({
color: '#3a8589',
height: 3,
padding: '13px 0',
'& .MuiSlider-thumb':
{
height: 20,
width: 20,
backgroundColor: '#fff',
border: '1px solid currentColor',
'&:hover': {
boxShadow: '0 0 0 8px rgba(58, 133, 137, 0.16)',
},
'& .checkmark-bar':
{
height: 9,
width: 1,
backgroundColor: 'currentColor',
marginLeft: 1,
marginRight: 1,
},
},
'& .MuiSlider-track':
{
height: 3,
},
'& .MuiSlider-rail':
{
color: theme.palette.mode === 'dark' ? '#bfbfbf' : '#d8d8d8',
opacity: theme.palette.mode === 'dark' ? undefined : 1,
height: 3,
},
}))
const CheckMarkThumbComponent = (props) =>
{
const { children, ...other } = props
return (
<SliderThumb {...other}>
{children}
<CheckCircleIcon />
</SliderThumb>
)
}
const CustomizedSlider = () =>
{
const [value, setValue] = React.useState(20)
React.useEffect(() =>
{
const intervalId = setInterval(() => setValue(Math.random() * 100), 500)
return () => clearInterval(intervalId)
}, [value])
return (
<Box sx={{ width: 320 }}>
<CheckMarkSlider
value = {value}
disabled
components={{ Thumb: CheckMarkThumbComponent }} />
</Box>
)
}
export default CustomizedSlider
I'm a newbie to MUI/react and I've been trying to place an Icon button beside my input base form, however I'm struggling to make it so that the input form consumes all the available space in my search div. Instead my search icon wrapper is the one that takes majority of the space. I'm really confused what I'm doing wrong, Can someone please shed some light on me?
Here's my code:
import * as React from "react";
import { styled } from "#mui/material/styles";
import Box from "#mui/material/Box";
import InputBase from "#mui/material/InputBase";
import SearchIcon from "#mui/icons-material/Search";
import IconButton from "#mui/material/IconButton";
const Search = styled("div")(({ theme }) => ({
display: "flex",
position: "relative",
borderRadius: 30,
backgroundColor: "#ffffff",
border: "1px",
borderStyle: "solid",
borderColor: "#55597d",
// marginLeft: 10,
width: "auto",
".MuiInputBase-root": {
width: "100%",
},
}));
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
// position: 'absolute',
// pointerEvents: 'none',
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
// backgroundColor: 'black',
width: "100%",
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(0)})`,
paddingRight: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
},
}));
export default function SearchAppBar({
searchQuery,
setSearchQuery,
clearGenre,
onDropDownChange,
}) {
return (
<Box sx={{ flexGrow: 1 }}>
<Search
sx={{
width: { xs: "90vw", md: "50vw", lg: "30vw" },
margin: "auto",
marginBottom: "20px",
}}
>
<form action="/" method="get">
<StyledInputBase
defaultValue={searchQuery}
// placeholder="Search All Games…"
inputProps={{ "aria-label": "search" }}
type="search"
name="s"
id="site-search"
/>
</form>
<SearchIconWrapper>
<IconButton>
<SearchIcon style={{ color: "#55597d" }} />
</IconButton>
</SearchIconWrapper>
</Search>
</Box>
);
}
You need to remove the width: 100% in the SearchIconWrapper on the right first. And because the Search component is a flex container, you also need to add flexGrow: 1 to the first child (the form) so it can expand to fit the parent and push the icon to the far right:
const Search = styled("div")(({ theme }) => ({
display: "flex",
position: "relative",
borderRadius: 30,
backgroundColor: "#ffffff",
border: "1px",
borderStyle: "solid",
borderColor: "#55597d",
// marginLeft: 10,
// ---------------------------------- add the following styles
"& :first-child": {
flexGrow: 1
}
width: "auto",
".MuiInputBase-root": {
width: "100%"
}
}));
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
// position: 'absolute',
// pointerEvents: 'none',
display: "flex",
alignItems: "center",
justifyContent: "flex-end"
// backgroundColor: 'black',
// -----------------------------------> comment this line: width: "100%"
}));
const formContainer = {
maxWidth: "300px",
backgroundColor: '#FD439B',
padding: "10px",
"& textarea" : {
width: '100%',
padding: '15px',
margin: '5px 0 22px 0',
border: "none",
background: '#F1F1F1',
resize: "none",
minHeight: '200px'
},
"& focus" : {
backgroundColor: '#ddd',
outline: 'none'
};
JS Code :
<div class="formContainer" style={formContainer}>
With this code only formContainer maxWidth,backgroundColor,padding is appending ,remaining code like textarea,focus styles are not coming.
do it with Material-UI styles :
import React from "react";
import { withStyles } from "#material-ui/core/styles";
const styles = theme => ({
root: {
maxWidth: "300px",
backgroundColor: '#FD439B',
padding: "10px",
"& textarea" : {
width: '100%',
padding: '15px',
margin: '5px 0 22px 0',
border: "none",
background: '#F1F1F1',
resize: "none",
minHeight: '200px'
},
"& textarea:focus" : {
backgroundColor: '#ddd',
outline: 'none'
}
}
});
function TypographyTheme(props) {
return (
<div className={props.classes.root} contenteditable="false">
<textarea>
"This div's text looks like that of a button.
</textarea>
</div>
);
}
export default withStyles(styles)(TypographyTheme);
Demo
this is my code:
const StyledTextInput = styled.TextInput`
background-color: red;
font-family: SF Pro Text Regular;
font-style: normal;
font-weight: normal;
font-size: 18px;
color: #000000;
align-items: flex-end;
justify-content: flex-end;
${(props) =>
props.error &&
`
border-bottom-color: red;
border-bottom-width: 1px;
`};
`;
basically what i am trying to do is increase height of text input but when it have text inside the text is in center, how do i make the text go on bottom?
Give a container to the TextInput and make that container act like the TextInput.
Also, use layout animation for a better experience.
Sample Code
import React, { Component } from 'react';
import {
View, Text, StyleSheet, TextInput, TouchableOpacity, LayoutAnimation, Platform, UIManager
} from 'react-native';
if (Platform.OS === 'android') {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
export default class Calls extends Component {
state={ bigger: false }
focus=() => {
this.textInput.focus();
}
toggle=() => {
const { bigger } = this.state;
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ bigger: !bigger });
}
render() {
const { bigger } = this.state;
return (
<View style={styles.container}>
<TouchableOpacity
onPress={this.focus}
style={[styles.inputContainer, { height: bigger ? 200 : 60 }]}
activeOpacity={1}
>
<TextInput
ref={(e) => { this.textInput = e; }}
defaultValue="ASWIN"
style={styles.input}
/>
</TouchableOpacity>
<TouchableOpacity onPress={this.toggle}>
<Text style={styles.toggle}>Toggle</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
inputContainer: {
width: '90%', justifyContent: 'flex-end', borderWidth: 1,
},
input: {
fontSize: 20, height: 50, width: '100%', padding: 0, paddingHorizontal: 10,
},
toggle: {
fontSize: 20,
marginTop: 10,
backgroundColor: '#00000099',
paddingHorizontal: 10,
paddingVertical: 5,
color: '#fff',
},
});
I am trying to use material ui's appbar and code in some custom responsiveness to it. I am creating a personal project that will be accessed from a Galaxy s8 smartphone. Ultimately I would like the nav-links and logout button on the navbar to consolidate into a hamburger button when the viewport is small. I am not quite sure how to achieve this however as I am using flexbox on the webpage when it is at full size and it keeps messing me up.
Here is the appbar at full size:
Here is the appbar at phone size with the hamburger button:
Here is the desired effect I would like with items, posts, groups, and the log out button being pushed to an accordion like menu on mobile:
Here is my navbar component code:
import { withStyles } from "#material-ui/core/styles";
import styles from "./styles/NavBarStyles";
import { LoggedInContext } from "./contexts/LoggedIn";
import { ThemeContext } from "./contexts/ThemeContext";
function Navbar(props) {
const { isDarkMode, toggleTheme } = useContext(ThemeContext);
const { loggedIn, changeLogIn, token, setToken } = useContext(
LoggedInContext
);
const { classes } = props;
const [mobileHamburgerOpen, setMobileHamburger] = useState(false);
const handleMobileClick = () => {
setMobileHamburger(!mobileHamburgerOpen);
console.log(mobileHamburgerOpen);
};
return (
<div className={classes.root}>
<AppBar
position="static"
style={{ background: isDarkMode ? "#2E3B55" : "#715AFF" }}
>
<Toolbar>
<Typography className={classes.title} variant="h6" color="inherit">
{isDarkMode ? "🌚" : "🌞"}
</Typography>
<Switch onChange={toggleTheme} />
<div className={classes.grow} />
{loggedIn && (
<div className={classes.loggedIn}>
<div className={classes.navlinks}>
<NavLink
to="/items"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
Items
</NavLink>
<NavLink
to="/facebookitems"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
<i className="fab fa-facebook-square"></i> Posts
</NavLink>
<NavLink
to="/groups"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
<i className="fab fa-facebook-square"></i> Groups
</NavLink>
</div>
<Button
className={classes.logOutButton}
variant="contained"
color="secondary"
onClick={handleClick}
>
Log Out
</Button>
<Button
className={classes.hamburgerMenu}
variant="contained"
color="primary"
onClick={handleMobileClick}
>
<i className="fas fa-bars"></i>
</Button>
</div>
)}
</Toolbar>
</AppBar>
</div>
);
}
export default withStyles(styles)(Navbar);
Here is my style sheet code:
import { fade } from "#material-ui/core/styles/colorManipulator";
const styles = theme => ({
root: {
width: "100%",
marginBottom: 0
},
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
title: {
display: 'block'
},
search: {
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: fade(theme.palette.common.white, 0.25)
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto"
}
},
searchIcon: {
width: theme.spacing(9),
height: "100%",
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
inputRoot: {
color: "inherit",
width: "100%"
},
inputInput: {
paddingTop: theme.spacing(1),
paddingRight: theme.spacing(1),
paddingBottom: theme.spacing(1),
paddingLeft: theme.spacing(10),
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: 120,
"&:focus": {
width: 200
}
}
},
loggedIn: {
width: "100%",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
paddingLeft: "2rem"
},
navlinks: {
// [theme.breakpoints.down("sm")]: {
// position: 'absolute',
// backgroundColor: "rgba(0,0,0,.7)",
// top: '0%',
// right: '0%',
// width: '100vw',
// height: '100vh'
// },
"& a": {
color: "white",
fontWeight: "bold",
fontSize: "1.5rem",
textDecoration: "none",
marginRight: '1rem',
padding: ".5rem",
borderRadius: "15px"
},
"& a:active": {
backgroundColor: "rgba(0, 0, 0, 0.5)"
}
},
hamburgerMenu: {
display: 'none',
[theme.breakpoints.down("sm")]: {
display: 'block',
marginLeft: "auto",
backgroundColor: "white",
color: "#715AFF"
}
},
logOutButton: {
// [theme.breakpoints.down("sm")]: {
// display: 'none'
// }
}
});
export default styles;