JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

CSS

body {
  margin: 0;
}

JavaScript

import { html, render, useState, useEffect, useCallback } from "https://unpkg.com/htm/preact/standalone.module.js";

function useVars() {
	// a^2 + b^2 + c^2 = 1
	const [a, setA] = useState(1);
  const [b, setB] = useState(0);
  const [c, setC] = useState(0);
  
  return {
  	a,
    b,
    c,
    setA(a) {
      if (a < -1) a = -1;
    	if (a > 1) a = 1;

    	setA(a);
      
      const sum = 1 - a ** 2;
      setB(sum * b / (b + c));
      setC(sum * c / (b + c));
    },
    setB(b) {
    	if (b < -1) b = -1;
      if (b > 1) b = 1;

      setB(b);
      
      const sum = 1 - b ** 2;
      setA(sum / 2);
      setC(sum / 2);
    },
    setC(c) {
    	if (c < -1) c = -1;
      if (c > 1) c = 1;
      
      setC(c);
      
      const sum = 1 - c ** 2;
      setA(sum / 2);
      setB(sum / 2);
    }
  }
}

function App() {
	const { a, b, c, setA, setB, setC } = useVars();

	return html`
  	<div
    	style=${{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-around",
        width: "100vw",
        height: "100vh"
    	}}
    >
    	<${Bar}
      	color="red"
      	value=${a}
        setValue=${setA}
      />
      <${Bar}
      	color="blue"
      	value=${b}
        setValue=${setB}
      />
      <${Bar}
      	color="purple"
      	value=${c}
        setValue=${setC}
      />
    </div>
  `;
}

function Bar({ value, setValue, color }) {
	const [dragStart, setDragStart] = useState(null);
  
  useCursor(dragStart ? "grabbing" : null);
  
	const onMouseDown = useCallback((event) => {
  	event.preventDefault();
  	setDragStart({
      mouseY: event.clientY,
      value: value
    });
  }, [value]);
  
  const onMouseMove = useCallback((event) => {
  	if (dragStart !== null) {
    	const deltaY = event.clientY - dragStart.mouseY;
      let newValue = dragStart.value - 0.005 * deltaY;
    	setValue(newValue);
    }
  }, [dragStart, setValue]);
  
  const onMouseUp = useCallback((event) => {
  	setDragStart(null);
  }, []);
  
...