Gradient border on MUI component - css

I did not manage to find the equivalent of border-image-source css
My goal is to render a border gradient on a button component

This is how you add a gradient border to the button component:
V5
const options = {
shouldForwardProp: (prop) => prop !== 'gradientColors',
};
const GradientButton = styled(
Button,
options,
)(({ theme, gradientColors }) => ({
border: '5px solid',
borderImageSlice: 1,
borderImageSource: `linear-gradient(to left, ${gradientColors.join(',')})`,
}));
If you want a round button with gradient border, you can't use the code above since borderImage doesn't have a radius. A workaround is to create a gradient background in the child element :after because the background can be clipped using borderRadius:
const borderRadius = 15;
const RoundGradientButton = styled(
Button,
options,
)(({ theme, gradientColors }) => ({
position: 'relative',
border: '5px solid transparent',
backgroundClip: 'padding-box',
borderRadius,
'&:after': {
position: 'absolute',
top: -5,
left: -5,
right: -5,
bottom: -5,
background: `linear-gradient(to left, ${gradientColors.join(',')})`,
content: '""',
zIndex: -1,
borderRadius,
},
}));
Usage
<GradientButton gradientColors={['red', 'yellow']} variant="contained">
Default
</GradientButton>
<RoundGradientButton gradientColors={['red', 'yellow']} variant="contained">
Default
</RoundGradientButton>
Live Demo
V4
const useStyles = makeStyles((theme: Theme) => ({
button: {
border: "6px solid",
borderImageSlice: 1,
borderImageSource: "linear-gradient(to left, red, orange)"
}
}));
export default function ContainedButtons() {
const classes = useStyles();
return (
<Button className={classes.button} variant="contained">
Default
</Button>
);
}
Live Demo
Related Answer
How to add linear-gradient color to MUI Chip background?

Related

React Beautiful DnD delay when dropping

I am using react-beautiful-dnd. I have it working, except when I drag and drag an item into one of my lists, the item is positioned incorrrectly, has a short delay, then jumps to the correct position.
Here is what it looks like: Link to issue
As you can see, after the item is dropped in a list, the item readjusts itself to fit within the div.
Here is the code for the item:
import React, { useState } from "react";
import styled from "styled-components";
import { Draggable } from "react-beautiful-dnd";
const Container = styled.div`
margin: 0 0 8px 0;
background-color: rgba(140, 240, 255);
`;
const Title = styled.div`
font-size: 1.5rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 0.5rem;
`;
const Description = styled.div`
font-size: 1rem;
`;
const Ticket = ({ ticket, setCategories, id, index }) => {
const [isDeleted, setIsDeleted] = useState(false);
const handleDelete = (e) => {
e.preventDefault();
fetch(`/tickets/${ticket.id}`, {
method: "DELETE",
}).then(
fetch("/categories")
.then((r) => r.json())
.then(setCategories)
);
setIsDeleted(true);
};
return (
<Draggable draggableId={id.toString()} index={index}>
{(provided, snapshot) =>
isDeleted ? null : (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Container
style={{
backgroundColor: snapshot.isDragging
? "aquamarine"
: "rgba(140, 240, 255)",
}}
>
<Title>{ticket.title}</Title>
<Gradient></Gradient>
<Description>{ticket.description}</Description>
<button onClick={handleDelete}>Delete</button>
</Container>
</div>
)
}
</Draggable>
);
};
export default Ticket;
And here is for the list:
import React, { useState } from "react";
import styled from "styled-components";
import Ticket from "./Ticket";
import { Droppable } from "react-beautiful-dnd";
import { transformData } from "./Categories";
const Container = styled.div`
background-color: rgba(255, 255, 255, 0.8);
border-radius: 0.25em;
box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
text-align: center;
width: 20rem;
font-size: 1.5rem;
padding: 4px;
margin: 1rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 1rem;
`;
const FormContainer = styled.div`
margin: 1rem;
border: 1px solid black;
backgroundColor: rgba(140, 240, 255);
`;
const Button = styled.button`
margin-left: 1rem;
`;
const DropDiv = styled.div`
min-height: 50vh;
padding: 4px;
`;
const Category = ({ category, user, setCategories, id }) => {
const [isClicked, setIsClicked] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const newTicket = {
title: title,
description: description,
user_id: user.id,
category_id: id,
};
fetch("/tickets", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newTicket),
}).then(
fetch("/categories")
.then((r) => r.json())
.then(transformData)
.then(setCategories)
);
setIsClicked(false);
};
return (
<Container>
{category.title}
<Button onClick={() => setIsClicked(!isClicked)}>Add</Button>
<Gradient></Gradient>
{isClicked ? (
<FormContainer>
<form onSubmit={handleSubmit}>
<label>Title</label>
<input onChange={(e) => setTitle(e.target.value)}></input>
<label>Description</label>
<input onChange={(e) => setDescription(e.target.value)}></input>
<button type="submit">Submit</button>
</form>
</FormContainer>
) : null}
<Droppable droppableId={id.toString()}>
{(provided, snapshot) => (
<DropDiv
{...provided.droppableProps}
ref={provided.innerRef}
style={{
background: snapshot.isDraggingOver ? "lightblue" : "",
}}
>
{category.tickets.map((ticket, index) => {
return (
<Ticket
ticket={ticket}
key={ticket.id}
setCategories={setCategories}
id={ticket.id}
index={index}
/>
);
})}
{provided.placeholder}
</DropDiv>
)}
</Droppable>
</Container>
);
};
export default Category;
I have tried flexbox styling and messed with margin and padding. If i remove the margin and padding it seems to go away, but in beautiful-dnd examples they all have space between items and theres no delay like this. Does anyone have any ideas?
It looks like the placeholder might not have the 8px bottom margin that the rest of the Draggables have.
You'll notice that when you pick up something (without changing it's position) from somewhere other than the end of the list, the list will shift up a bit right away, and when you drop something at the end of a list, you don't see the issue.
The placeholder gets its margins from the item being dragged. You can see this happening by looking at the inline styles on the placeholder element that appears at the end of the droppable while you are dragging.
So, you might want to try putting the provided innerRef, draggableProps, and dragHandleProps on the Ticket Container itself instead of a parent div, as it's possible that because they are on a different element, react-beautiful-dnd isn't taking the margins in to account.
The delay could be caused because your list is changing size while dragging an element, and this provokes the library to recalculate and animate slower.
The solution is to avoid this size change while dragging.
The causes of the issue could be:
---- Cause A ----
The {provided.placeholder} auto inserted in the DOM while dragging doesn't have the same margins/padding that the other Draggable elements
Solution
Be sure to add the styles that separate the elements (margin/padding) to the element that you are applying the provided innerRef, draggableProps, and dragHandleProps because in this way the {provided.placeholder} will inherit those styles.
---- Cause B ----
You are using flexbox or css-grid with a gap property to separate the elements.
Solution
Stop using gap and just add margin to the element with provided innerRef, draggableProps, and dragHandleProps (do not use inline styles but css classes)
Extra: To confirm that size change is the cause
Drag some elements from somewhere other than the end of the list and notice how another element will move up a bit.
Also, when you drop something at the end of a list, you don't see the problem.
I was having this same issue- but as seen in the docs, inline styling with provided props did the trick for me:
const ListItem = ({ item, index }) => {
return (
<Draggable draggableId={item.id} className="draggableItem" index={index}>
{(provided, snapshot) => {
return (
<div
ref={provided.innerRef}
snapshot={snapshot}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
userSelect: "none",
padding: 12,
margin: "0 0 8px 0",
minHeight: "50px",
borderRadius: "4px",
backgroundColor: snapshot.isDragging
? "rgb(247, 247, 247)"
: "#fff",
...provided.draggableProps.style,
}}
>
<div className="cardHeader">Header</div>
<span>Content</span>
<div className="cardFooter">
<span>{item.content}</span>
<div className="author">
{item.id}
<img className="avatar" />
</div>
</div>
</div>
);
}}
</Draggable>
);
};

How to place icon at the end in linear progress bar MUI?

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

Add styling to defaultValue in textarea in React

I am using a Material UI React component called TextareaAutosize:
<TextareaAutosize
minRows={2}
style={resize: 'none'}
defaultValue={<span style={{fontSize: "20px", color: "blue"}}>Content Body</span>}
/>
Instead of getting Content Body in the TextareaAutosize component, I'm getting this as the default value:
How do I add styling to the defaultValue?
Edit code on Stack Blitz: https://stackblitz.com/edit/react-fnx6we?file=demo.js
EDIT: For clarification, I want ONLY the defaultValue to have the styling I applied. When the user starts typing or removes the defaultValue and starts typing, the styling of the defaultValue should NOT be applied.
defaultValue parameter accepts String values only. Use parameter style to define additional styling. Or use jss like styling for components (check #mui/styles) EDIT: to change styling of the element "on the fly" you will need to use additional variables and functions. Check demo on code on Stack Blitz.
import React, { useState } from 'react';
import TextareaAutosize from '#mui/material/TextareaAutosize';
import { makeStyles } from '#mui/styles';
const useStyles = makeStyles({
textAreaWithStyle: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
fontSize: '20px',
color: 'blue',
resize: 'none',
},
textAreaWithoutStyle: {
resize: 'none',
},
});
export default function MaxHeightTextarea() {
const classes = useStyles();
const [valueOfInput, setValueOfInput] = useState('Default Text');
const returnStyleBasedOnInput = () => {
if (valueOfInput === 'Default Text') {
return classes.textAreaWithStyle;
} else {
return classes.textAreaWithoutStyle;
}
};
const checkIfDefaultValueInTextAreaAndRemooveIt = () => {
if (valueOfInput === 'Default Text') {
setValueOfInput('');
}
};
const onInputChange = (e) => {
setValueOfInput(e.target.value);
};
return (
<TextareaAutosize
maxRows={4}
className={returnStyleBasedOnInput()}
value={valueOfInput}
onChange={onInputChange}
onClick={checkIfDefaultValueInTextAreaAndRemooveIt}
/>
);
}
Edit code on Stack Blitz: https://stackblitz.com/edit/react-fnx6we-3vdn7i?file=demo.js

custom styling for material UI tooltip arrow?

I would like to add a custom style for Material UI tooltip arrow but I can not set the border color and the background color.
This is the configuration I have - react:
const useStylesBootstrap = makeStyles(theme => ({
arrow: {
// color: '#E6E8ED',
border: '1px solid #E6E8ED',
},
tooltip: {
backgroundColor: theme.palette.common.white,
border: '1px solid #E6E8ED',
color: '#4A4A4A'
},
}));
This is what I want to achieve:
I want to apply a gray color in the triangle border and the background will be white.
On the arrow configuration, the border config will not work, it will apply a border color in the square that's housing the triangle. Without material UI, the issue could be solved using the pseudo :before and :after to achieve the desired output. I would like to know if there is a solution to this using material UI custom configuration. Not too familiar with Material UI, your help will be appreciated
You are right, You need to override &:before pseudoselector like this.
Here is the code sandbox project link
import React from "react";
import Button from "#material-ui/core/Button";
import Tooltip from "#material-ui/core/Tooltip";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
arrow: {
"&:before": {
border: "1px solid #E6E8ED"
},
color: theme.palette.common.white
},
tooltip: {
backgroundColor: theme.palette.common.white,
border: "1px solid #E6E8ED",
color: "#4A4A4A"
}
}));
export default function ArrowTooltips() {
let classes = useStyles();
return (
<Tooltip
title="Add"
arrow
classes={{ arrow: classes.arrow, tooltip: classes.tooltip }}
>
<Button>Arrow</Button>
</Tooltip>
);
}
See tooltip css. Use arrow and &::before to target the arrow and apply your styles. (note the double :: there)
makeStyles - style
arrow: {
fontSize: 20,
color: "#4A4A4A",
"&::before": {
backgroundColor: "blue",
border: "2px solid red"
}
}
JSX
<Tooltip classes={{ arrow: classes.arrow }} title="Delete" arrow>
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
</Tooltip>
Working demo
FYI on material ui 5 makestyles is deprecated.
Because tooltip is in portal you cannot style it directly
const StyledTooltip = styled<typeof Tooltip>(({ className, ...props }) => (
<Tooltip {...props} classes={{ popper: className }} />
))``;
then in reder function you can use sx, by setting popper you can access child props via sx
<StyledTooltip
open
arrow
sx={{
'& .MuiTooltip-arrow': {
background: 'red',
},
}}
/>
Using the official MUI customization examples:
https://mui.com/material-ui/react-tooltip/#customization
const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.arrow}`]: {
color: theme.palette.common.white,
"&::before": {
backgroundColor: theme.palette.common.white,
border: "1px solid #999"
}
},
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: theme.palette.common.white,
color: 'rgba(0, 0, 0, 0.87)',
boxShadow: theme.shadows[1],
fontSize: 11,
},
}));
We can do a custom styling in the following way
import Tooltip from '#material-ui/core/Tooltip'
import { withStyles } from '#material-ui/core/styles'
const HtmlTooltip = withStyles(theme => ({
arrow: {
'&::before': {
color: 'white'
}
},
tooltip: {
backgroundColor: '#f5f5f9',
boxShadow: theme.shadows[8],
color: 'rgba(0, 0, 0, 0.87)',
fontSize: 14,
maxWidth: 800,
padding: 0,
},
tooltipPlacementTop: {
margin: '4px 0',
},
}))(Tooltip)
<HtmlTooltip
title={
<React.Fragment>
<Typography color="inherit">Tooltip with HTML</Typography>
<em>{"And here's"}</em> <b>{'some'}</b> <u>{'amazing content'}</u>.{' '}
{"It's very engaging. Right?"}
</React.Fragment>
}
>
<Button>HTML</Button>
</HtmlTooltip>

Material-UI style buttons on the right

How do you align buttons on the right using Material-UI's makeStyles function?
I have tried using CSS's margin-right: 0 tag, but there is an error using '-' with makeStyles.
I renamed it as 'marginRight' and it still does not work. Also mr: 0 is not valid either. (Using Material-UI's spacing).
The code is trying to make the UI similar to stackOverflow's title layout.
import React from 'react';
import { makeStyles } from "#material-ui/core/styles";
import { Box, Button } from "#material-ui/core";
const style = makeStyles({
titleItemRight: {
color: 'white',
backgroundColor: 'blue',
top: '50%',
height: 30,
align: 'right',
position: 'relative',
transform: 'translateY(-50%)',
}
});
const App = () => {
const classes = style();
return (
<div>
<Box className={classes.titleBar}>
<Button variant='text' className={classes.titleItemRight}>Sign In</Button>
</Box>
</div>
);
};
Change,
align: 'right'
To,
float: 'right'
So the code would look like,
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Box, Button } from "#material-ui/core";
const style = makeStyles({
titleItemRight: {
color: "white",
backgroundColor: "blue",
top: "50%",
height: 30,
float: "right",
position: "relative",
transform: "translateY(-50%)"
}
});
const App = () => {
const classes = style();
return (
<div>
<Box className={classes.titleBar}>
<Button variant="text" className={classes.titleItemRight}>
Sign In
</Button>
</Box>
</div>
);
};
Working Codesandbox
I'd suggest using a flexbox for this or just using the AppBar provided already by material ui
https://material-ui.com/components/app-bar/#app-bar
if you'd still like to use Box, just edit the titleBar styles this way and add a spacer element to seperate elements to far right or far left
const style = makeStyles({
titleBar: {
display: 'flex',
width:'100%',
flexFlow: 'row',
},
spacer: {
flex: '1 1 auto'
}
});
and then your component
<Box className={classes.titleBar}>
<LogoHere/>
<div className={classes.spacer}/>
<Button variant="text">
Sign In
</Button>
</Box>

Resources