ColorPicker material-ui-color, set color on mouse up

Viewed 681

I am using ColorPicker from material-ui-color and I have a problem with setting value onChange.

The issue is that when I start dragging the mouse in ColorPicker it constantly calls onChange. I would expect that the moment I stop dragging the mouse it would call onChange.

Any ideas on how to accomplish desired result? Link to CodeSandBox

2 Answers

Try adding deferred prop in the ColorPicker. With deferred set to true, change handler only fires when you click the SET button to commit the change:

<ColorPicker value={color} onChange={handleChange} deferred />

EDIT: If you really really want to listen to the mouseup event, you need to do some dirty work using plain JS:

function MyColorPicker({ onChange }) {
  const [open, setOpen] = useState(false);
  const [, forceRender] = useReducer((x) => ++x, 0);
  const colorRef = useRef<Color>();
  const handleChange = (newValue: Color) => {
    colorRef.current = newValue;
    forceRender();
  };

  React.useEffect(() => {
    let colorGradientEl = null;
    let hueSliderEl = null;
    let alphaSliderEl = null;
    let inputEls = [];
    const handleMouseUp = () => onChange(colorRef.current);

    if (open) {
      setTimeout(() => {
        colorGradientEl = document.querySelector(
          '[data-testid="hsvgradient-color"]'
        );
        hueSliderEl = document.querySelector('[data-testid="hueslider"]');
        alphaSliderEl = document.querySelector('[data-testid="alphaslider"]');
        inputEls = [...document.querySelectorAll("input")];
        const colorPickerPopoverEl = document.querySelector(
          ".ColorPicker-MuiPopover-root"
        );

        const observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            if (mutation.removedNodes) {
              setOpen(false);
            }
            observer.disconnect();
          });
        });

        observer.observe(colorPickerPopoverEl!, { childList: true });
        colorGradientEl?.addEventListener("mouseup", handleMouseUp);
        hueSliderEl?.addEventListener("mouseup", handleMouseUp);
        alphaSliderEl?.addEventListener("mouseup", handleMouseUp);

        inputEls.forEach((el) => {
          el.addEventListener("change", handleMouseUp);
        });
      }, 10);
    }

    return () => {
      colorGradientEl?.removeEventListener("mouseup", handleMouseUp);
      hueSliderEl?.removeEventListener("mouseup", handleMouseUp);
      alphaSliderEl?.removeEventListener("mouseup", handleMouseUp);

      inputEls.forEach((el) => {
        el.removeEventListener("change", handleMouseUp);
      });
    };
  }, [open]);

  return (
    <ColorPicker
      onOpen={() => setOpen(true)}
      value={colorRef.current}
      onChange={handleChange}
    />
  );
}

Usage

MyColorPicker now only fires change handler when the user stops dragging the mouse:

<MyColorPicker onChange={handleChange} />

Codesandbox Demo

With the help of useEffect we can perform actual setState or in this case console.log(color), some time (in this case after 0.2 sec) after the user stops dragging the mouse in colorPicker or moving the slider in opacity or etc.

useEffect(() => {
    const timeoutId = setTimeout(
      () => {
        if (color != null) {
          //Do your things
          console.log(color);
        }
      },

      200
    );
    return () => clearTimeout(timeoutId);
  }, [color]);

CodeSandbox

Related