I'm trying to make an input expand when I press the search button, but I can't change the material-ui style after using makeStyles.
Is there a way I can do it?
Thanks
Sample code:
const useStyles = makeStyles((theme) => ({
searchInput: {
width: 0,
padding: 0,
border: "none",
},
}));
function ExpandableSearchBar() {
const classes = useStyles();
function onClick() {
// change style of the input class
// classes.searchInput
}
return (
<div className="component-wrapper">
<IconButton onClick={onClick} aria-label="Search">
<SearchIcon />
</IconButton>
<input className={classes.searchInput} type="text" />
</div>
);
}
You can set a state to toggle className of the input. Your code should look like -
const useStyles = makeStyles((theme) => ({
searchInput: {
width: 0,
padding: 0,
border: "none",
},
expandedSearchInput: {
width: "5rem" // or, you can set it as per your need
// add other styles of expanded input
}
}));
function ExpandableSearchBar() {
const [isExpanded,setExpand] = useState(false);
const classes = useStyles();
function toggleSearchInput(){
setExpand(val => !val);
}
return (
<div className="component-wrapper">
<IconButton onClick={toggleSearchInput} aria-label="Search">
<SearchIcon />
</IconButton>
<input
className={isExpanded ? classes.expandedSearchInput : classes.searchInput}
type="text" />
</div>
);
}
Note - Have a look on the className of the <input/>. It will change to classes.expandedSearchInput when the isExpanded is set to true.
Related
By default, in React JS Material UI's Select component, when we provide a custom IconComponent, it gets turned upside down when user has selected the dropdown / Select component.
Sample code:
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={Search}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
...
I did a sneaky thing to remove "MuiSelect-iconOpen" from the className when calling IconComponent.
Sample Code after my fix:
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={({ className }) => {
className = className.replace("MuiSelect-iconOpen", "")
return <Search className={className} />
}}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
....
Now is there a better way to do this without replacing the className?
My current solution is to overwrite the original iconOpen class provided by the Material-UI Select.
....
import { makeStyles } from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
iconOpen: {
transform: 'rotate(0deg)',
},
}));
....
export const MyCompo: FC<> = () => {
const classes = useStyles();
return (
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={Search}
classes={{
iconOpen: classes.iconOpen,
}}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
....
<Select
value={values.phoneCode}
onChange={handleChange("phoneCode")}
inputProps={{ "aria-label": "Without label" }}
IconComponent={(_props) => {
const rotate = _props.className.toString().includes("iconOpen");
return (
<div
style={{
position: "absolute",
cursor: "pointer",
pointerEvents: "none",
right: 10,
height: "15px",
width: "15px",
transform: rotate ? "rotate(180deg)" : "none",
}}
>
<ArrowDown />
</div>
);
}}
>
....
It does not rotate if you use arrow function: IconComponent={()=> <YourIcon/>}
Haven't made this feature before where you can change the color of button's hover.
I have already made a feature to change the radius with a slider, background color and font color using color-picker. However, I noticed the hover (for background AND font) could be better.
Here is the code:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import Slider from "#material-ui/core/Slider";
import Input from "#material-ui/core/Input";
import Button from "#material-ui/core/Button";
import { ChromePicker } from "react-color";
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1)
}
},
Button: {
width: 150,
height: 50,
borderRadius: "var(--borderRadius)"
},
color: {
width: "36px",
height: "14px",
borderRadius: "2px"
},
swatch: {
padding: "5px",
background: "#fff",
borderRadius: "1px",
display: "inline-block",
cursor: "pointer"
},
popover: {
position: "absolute",
zIndex: "2"
},
cover: {
position: "fixed",
top: "0px",
right: "0px",
bottom: "0px",
left: "0px"
}
}));
export default function InputSlider() {
const classes = useStyles();
const [value, setValue] = React.useState(30);
const [color, setColor] = React.useState({ r: 0, g: 0, b: 0, a: 1 });
const [fontColor, setFontColor] = React.useState({
r: 255,
g: 255,
b: 255,
a: 1
});
const [displayColorPicker, setDisplayColorPicker] = React.useState(true);
const handleSliderChange = (event, newValue) => {
setValue(newValue);
};
const handleInputChange = (event) => {
setValue(event.target.value === "" ? "" : Number(event.target.value));
};
const handleBlur = () => {
if (value < 0) {
setValue(0);
} else if (value > 30) {
setValue(30);
}
};
const handleClick = () => {
setDisplayColorPicker(!displayColorPicker);
};
const handleClose = () => {
setDisplayColorPicker(false);
};
const handleChange = (color) => {
setColor(color.rgb);
};
const handleFontColorChange = (color) => {
setFontColor(color.rgb);
};
return (
<div className={classes.root}>
<style>
{`:root {
--borderRadius = ${value}px;
}`}
</style>
<Button
style={{
borderRadius: value,
background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`,
color: `rgba(${fontColor.r}, ${fontColor.g}, ${fontColor.b}, ${fontColor.a})`
}}
variant="contained"
color="primary"
value="value"
onChange={handleSliderChange}
className={classes.Button}
>
Fire laser
</Button>
<Grid container spacing={2}>
<Grid item xs>
<Slider
value={typeof value === "number" ? value : 0}
onChange={handleSliderChange}
aria-labelledby="input-slider"
/>
</Grid>
<Grid item>
<Input
value={value}
margin="dense"
onChange={handleInputChange}
onBlur={handleBlur}
inputProps={{
step: 10,
min: 0,
max: 24,
type: "number"
}}
/>
</Grid>
</Grid>
<div>
<div style={useStyles.swatch} onClick={handleClick}>
{displayColorPicker} <p class="h4">Background</p>
<div style={useStyles.color} />
</div>
{displayColorPicker ? (
<div style={useStyles.popover}>
<div style={useStyles.cover} onClick={handleClose}></div>
<ChromePicker color={color} onChange={handleChange} />
</div>
) : null}
</div>
<div>
<div style={useStyles.swatch} onClick={handleClick}>
{displayColorPicker} <p class="h4">Font</p>
<div style={useStyles.color} />
</div>
{displayColorPicker ? (
<div style={useStyles.popover}>
<div style={useStyles.cover} onClick={handleClose}></div>
<ChromePicker color={fontColor} onChange={handleFontColorChange} />
</div>
) : null}
</div>
</div>
);
}
And here is the sandbox - https://codesandbox.io/s/material-demo-forked-t8xut?file=/demo.js
Any advice?
Does anyone have a good Material UI article for editing/cool features and projects to play with?
You need to pass props to makeStyles.
First, pass fontColor variable as below when declaring classes:
const classes = useStyles({ hoverBackgroundColor, hoverFontColor })();
then in the useStyles, you can have access to the fontColor as a prop, as below:
const useStyles = ({ hoverBackgroundColor, hoverFontColor }) =>
makeStyles((theme) => ({
Button: {
width: 150,
height: 50,
borderRadius: "var(--borderRadius)",
"&:hover": {
backgroundColor: `rgba(${hoverBackgroundColor.r}, ${hoverBackgroundColor.g}, ${hoverBackgroundColor.b}, ${hoverBackgroundColor.a}) !important`,
color: `rgba(${hoverFontColor.r}, ${hoverFontColor.g}, ${hoverFontColor.b}, ${hoverFontColor.a}) !important`
}
},
sandbox
I have multiple external makestyle that I want to combine. So, I can organize the style per component. The single makestyle is also good but the length of the file is too much.
I saw this on material ui documentation but it's not working
Makestyle
import useStyles from '../styles/style';
import useAddTaskStyles from '../styles/addTaskStyle';
const classes = useStyles();
const classesAddTask = useAddTaskStyles();
const className = clsx(classes, classesAddTask);
<Modal
className={className.modalContainer}
open={showAddTask}
onClose={handleCloseAddTask}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={showAddTask}>
<Tasks ref={useRef} />
</Fade>
</Modal>
import useStyles from '../styles/style';
import useAddTaskStyles from '../styles/addTaskStyle';
const classes = useStyles();
const classesAddTask = useAddTaskStyles();
<Modal
className={`${classes.modalContainer} ${classesAddTask.modalContainer}`}
open={showAddTask}
onClose={handleCloseAddTask}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={showAddTask}>
<Tasks ref={useRef} />
</Fade>
</Modal>;
You may combine it like this
const className = { ...classes, ...classesAddTask }
Codesandbox => https://codesandbox.io/s/nice-fire-xgvz2?file=/src/App.js
For Example (creating a useStyle hook that combines all style hooks)
import { makeStyles } from "#material-ui/styles";
const useStyles1 = makeStyles({
red: {
backgroundColor: "red"
}
});
const useStyles2 = makeStyles({
blue: {
backgroundColor: "blue"
}
});
const useStyles = () => {
const classes = useStyles1();
const classes2 = useStyles2();
return { ...classes, ...classes2 };
};
export default function App() {
const classes = useStyles();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div className={classes.blue} style={{ width: "200px", height: "200px" }}>
blue box
</div>
<div className={classes.red} style={{ width: "200px", height: "200px" }}>
red box
</div>
</div>
);
}
I have a form component with an input.
const CustomInput = (props) => {
const classes = useStyles(props);
return (
<FormControl className={classes.form}>
<Input
classes={{ input: classes.input, underline: classes.underline }}
/>
</FormControl>
);
};
In the useStyles I want to control the color of input underline (borderColor property) by setting it from the props:
const useStyles = makeStyles((theme) => ({
underline: (props) => ({
'&:hover:not($disabled):before,&:before': {
borderColor: '#D2D2D2 !important',
borderWidth: '1px !important'
},
'&:after': {
borderColor: theme.palette.secondary.main,
borderColor: props.borderColor
}
})
}));
However, when I do pass the property in another component App.js (shown below), it seems to be undefined and the color doesn't change. What am I missing here?
I have looked through similar questions here but still can't get it.
const useStyles = makeStyles((theme) => ({
underline: {
borderColor: theme.palette.secondary.main
}
}));
const App = () => {
const classes = useStyles();
return <CustomInput
labelText="Custom Input"
className={`${classes.underline}`}
inputStyles
/>
}
You may be mixing className with passing props to useStyles. What you could do in the App component is passing a borderColor prop to CustomInput:
const App = () => {
return (
<CustomInput labelText="Custom Input" borderColor="green" inputStyles />
);
};
If you want to override the child component's style with className, you have to pass the className as a prop and in the child component, add to its element's className:
const CustomInput = ({ className, borderColor }) => {
const classes = useStyles({ borderColor });
return (
<FormControl className={`${classes.form} ${className}`}> // <--
<Input classes={{ input: classes.input, underline: classes.underline }} />
</FormControl>
);
};
As a side note, you can access to the theme by using a function signature for makeStyles or check out Material UI useTheme hook. Docs
I want to change on the image slider used with UI material when I drag on the slider, but it changes only grayscale but nothing happens on the slider, why?
I will try to do function but I don't have idea how to do? somebody have?
import React, { useState, useEffect } from 'react';
import logo from '../logo.svg';
import defaultImage from '../Image/sen.jpg';
import Typography from "#material-ui/core/Typography";
import Slider from "#material-ui/lab/Slider";
function ImageSlider ({ value, max, onChange, children }) {
return (
<>
<Typography id="label">
{children}
</Typography>
<Slider className="slider"
min={0}
max={max}
value={value}
aria-labelledby="label"
step={1}
onChange={onChange}
/>
</>
)
}
export default function Hooks () {
const [name, setName] = useState('Franek!');
const [contrast, setContrast] = useState('100%');
const [brightness, setBrightness] = useState('100%');
const [invert, setInvert] = useState("0%");
const [hue, setHue] = useState("0deg");
const [saturate, setSaturate] = useState("100%");
const [sepia, setSepia] = useState("0%");
const [grayscale, setGrayscale] = useState('0%');
const [rotation, setRotation] = useState("0deg");
const [width, setWidth] = useState('0');
const [height, setHeight] = useState('0');
const [color, setColor] = useState('black');
const container = {
display: 'grid',
gridTemplateColumns: 'auto auto auto',
gridTemplateRows: '80px 200px',
gridGap: '200px',
padding: '10px'
}
const settings = {
width: '200px',
maxHeight: '1000px'
}
const buttonStyle = {
height: '50px',
width: '200px'
}
const parametersStyle = {
height: '50px',
width: '100px',
marginBlockEnd: '0',
marginBlockStart: '0',
backgroundColor: 'rgba(46, 56, 79, 0.85)',
padding: '1em'
}
const imgStyle = {
width: '300px',
height: '300px',
transform: `rotate(${rotation})`,
filter: `sepia(${sepia}) grayscale(${grayscale}) hue-rotate(${hue}) saturate(${saturate}) invert(${invert}) contrast(${contrast}) brightness(${brightness})`,
color: color
}
const elementChangingStyle = {
maxWidth: '600px',
maxHeight: '600px'
}
const headerTitle = {
color: '#ffffff',
fontSize: '40px',
padding: '1em'
}
// thiw function but they are get only 50 and see
function onGrayscale (e, grayscale) {
let newGrey = grayscale;
console.log("this onGrayscale " + setGrayscale('50'));
}
return (
<div>
<div style={headerTitle}>
React Photo-Modifier <br/> with Hooks
</div>
<div style={container}>
<div style={settings}>
<ImageSlider
max={100}
value={grayscale}
onChange={e => setGrayscale(e.target.value)}
>
Grayscale {grayscale}
</ImageSlider>
</div>
<div style={elementChangingStyle}>
<div>
<span>
<img src={logo} className="App-logo" alt="logo" />
</span>
</div>
<img style={imgStyle} src={defaultImage} />
<p style={imgStyle} > {name}</p>
</div>
</div>
</div>
)
}
If I triggered function onGrayscale the I have only slide to 50 but I want do this dynamically? How to do?
If I set ImageSlider to target value then change to grayscale but I can't then change manually using the slider?
What I'm doing wrong?
EDIT 1:
This. it's working now! Under the function and return in return.
function onGrayscale (e, grayscale) {
setGrayscale(grayscale);
}
<ImageSlider
max={100}
value={grayscale}
onChange={onGrayscale}
>
Grayscale {grayscale}
</ImageSlider>
You aren't using the function arguments correctly in onGrayScale function. This function is only passed the value and not the event, so it would look like
function onGrayscale (grayscale) {
let newGrey = grayscale;
setGrayscale(grayScale);
}