Search code examples
reactjsmaterial-uihovercolor-picker

Material UI Custom Hover Color


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?


Solution

  • 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