JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

CSS

body {
  font-family: sans-serif;
}

.PhasorBox {
  height: 200px;
  width: 200px;
}

.WaveBox {
  width: 400px;
  height: 200px;
}

.columns {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto;
  grid-gap: 16px;
}
.column {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.diagram {
  position: relative;
  border: 1px solid #ddd;
  box-shadow: 0 4px 8px #eee;
  border-radius: 12px;
  padding: 16px;
  margin-bottom: 16px;
  
  display: flex;
  justify-content: center;
}
.labels {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  
  display: flex;
  justify-content: flex-end;
  padding: 4px;
}
.label {
  font-size: 14px;
  font-family: monospace;
  color: white;
  padding: 2px 8px;
  min-width: 6px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 4px;
  cursor: pointer;
  user-select: none;
}
.label-hidden {
  opacity: 0.4;
}

JavaScript

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

const WaveBoxContext = createContext({ omega: 0, time: 0 });

function ACPhasorApplet() {
	const [time, timeVel, setTime] = useProgressValue(0, 0);
  
  const [omega, setOmega] = useState(250); // rad/s

  const [R, setR] = useState(250);
  const [L, setL] = useState(500);
  const [C, setC] = useState(20);
  
  const phi = Math.atan((omega*L/10**3 - 10**6/(omega*C)) / R);
  const angle = omega * time - phi;
  
  const V_0 = 250;
  const I = V_0 / Math.sqrt(
  	R**2 +
    (omega*L/10**3 - 10**6/(omega*C))**2
  );
  const V_R = I*R;
  const V_L = I*omega*L/10**3;
  const V_C = omega === 0 ? 0 : I**2 * 10**6/(omega*C);
  
  const vScale = 0.2;
  const iScale = vScale * 100;
  
  console.log(I);
  
  const [showI, setShowI] = useState(true);
  const [showR, setShowR] = useState(true);
  const [showL, setShowL] = useState(false);
  const [showC, setShowC] = useState(false);

	return html`
  	<div class="columns">
    	<div class="column">
        <div class="diagram">
          <${WaveBox} time=${time} omega=${omega} phi=${phi}>
            ${showL && html`<${Wave} dirDiff=${Math.PI / 2} mag=${V_L*vScale} color="#2fcc5b" />`}
            ${showR && html`<${Wave} dirDiff=${0} mag=${V_R*vScale} color="#0e8a30" />`}
            ${showC && html`<${Wave} dirDiff=${-Math.PI / 2} mag=${V_C*vScale} color="#043612" />`}

            ${showI && html`<${Wave} dirDiff=${0} mag=${I*iScale} color="#7d2b8b" />`}
          <//>
        </div>
        
        <div style=${{ width: 432, display: "flex", alignItems: "center" }}>
          <button
            onClick=${() => {
              if (timeVel === 0) {
                setTime(time, 0.1 / 5);
              } else {
                setTime(time, 0);
              }
            }}
          >
            ${timeVel === 0 ? "Play" : "Pause"}
          </button>

          <label style=${{ display:...