0

My eyes hurt and I am attempting to not only allow a slider to change the radius of a button but also the color.

This sandbox succeeds at updating the radius.

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 reactCSS from "reactcss";
import { SketchPicker } from "react-color";

const useStyles = makeStyles((theme) => ({
  root: {
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  Button: {
    width: 150,
    height: 50,
    borderRadius: "var(--borderRadius)"
  }
}));

export default function InputSlider() {
  const classes = useStyles();
  const [value, setValue] = React.useState(30);
  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);
    }
  };
  return (
    <div className={classes.root}>
      <style>
        {`:root {
          --borderRadius = ${value}px;
        }`}
      </style>
      <Button
        style={{ borderRadius: value }}
        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>
  );
}

NOW I am working on using color picker to change the button's background color and eventually the font color as well.

I am receiving an error message for my this.state. Advice?

Here is a different sandbox that is in progress and has the error message - https://codesandbox.io/s/material-demo-forked-l35qy?file=/demo.js

Here is the code with the error message:

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 reactCSS from "reactcss";
import { SketchPicker } from "react-color";

const useStyles = makeStyles((theme) => ({
  root: {
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  Button: {
    width: 150,
    height: 50,
    borderRadius: "var(--borderRadius)",
    background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
  },
  color: {
    width: "36px",
    height: "14px",
    borderRadius: "2px",
    background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
  },
  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 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 = () => {
    this.setState({ displayColorPicker: !this.state.displayColorPicker });
  };

  const handleClose = () => {
    this.setState({ displayColorPicker: false });
  };

  const handleChange = (color) => {
    this.setState({ color: color.rgb });
  };
  return (
    <div className={classes.root}>
      <style>
        {`:root {
          --borderRadius = ${value}px;
        }`}
      </style>
      <Button
        style={{ borderRadius: value }}
        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>
        <Button style={useStyles.color}></Button>

        <div style={useStyles.swatch} onClick={this.handleClick}>
          <div style={useStyles.color} />
        </div>
        {this.state.displayColorPicker ? (
          <div style={useStyles.popover}>
            <div style={useStyles.cover} onClick={this.handleClose} />
            <SketchPicker
              color={this.state.color}
              onChange={this.handleChange}
            />
          </div>
        ) : null}
      </div>
    </div>
  );
}
Myles
  • 57
  • 1
  • 9

2 Answers2

2

You cant use state inside of useStyles because you only get access to the theme and not any props. You should use the makeStyles call instead as that will at least let you pass in some props. This has already been answered here and there is a good example.

Passing props to material UI style

Another issue is that you are using a functional based component where states are written differently. I have rewritten it using the useState hook for you so hopefully this will at least getting the color to use.

export default function InputSlider() {
  const classes = useStyles();
  const [value, setValue] = React.useState(30);
  const [color, setColor] = React.useState({ background: "#fff" });

  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 = (color) => {
    setColor(color);
  };

  const handleClose = () => {
    setColor({ displayColorPicker: false });
  };

  const handleChange = (color) => {
    setColor(color);
  };
  return (
    <div className={classes.root}>
      <style>
        {`:root {
          --borderRadius = ${value}px;
        }`}
      </style>
      <Button
        style={{ borderRadius: value }}
        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>
        <Button style={useStyles.color}></Button>

        <div style={useStyles.swatch} onClick={handleClick}>
          <div style={useStyles.color} />
        </div>
        {color ? (
          <div style={useStyles.popover}>
            <div style={useStyles.cover} onClick={handleClose} />
            <SketchPicker color={color} onChange={handleChange} />
          </div>
        ) : null}
      </div>
    </div>
  );
}
Richard Hpa
  • 2,353
  • 14
  • 23
1

@RichardHpa is correct, you cannot access state in makeStyles and you mix up class component and functional component. In your case, you should use useState() all the way.

You already have the sample of how to set the border radius so you can copy that flow to use it in your background color where setting style on Button directly.

Here is the codesandbox:

https://codesandbox.io/s/material-demo-forked-djh78?file=/demo.js

<Button
    style={{
        borderRadius: value,
        background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`

        // may need another color picker for text coloe. otherwise, bg color will be the same as text color.
        // color: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
    }}
    variant="contained"
    color="primary"
    value="value"
    onChange={handleSliderChange}
    className={classes.Button}
>

No need to rate for me, @RichardHpa deserves the credit.

Mic Fung
  • 5,404
  • 2
  • 7
  • 17