Files @ a1f1653e3d5b
Branch filter:

Location: light9/web/fade/Light9Fader.ts

drewp@bigasterisk.com
new dep
import debug from "debug";
import { css, html, LitElement, PropertyValueMap } from "lit";
import { customElement, property, query } from "lit/decorators.js";

import { clamp } from "../floating_color_picker";
const log = debug("fade");

class Drag {
  constructor(public startDragPxY: number, public startDragValue: number) {}
}

@customElement("light9-fader")
export class Light9Fader extends LitElement {
  static styles = [
    css`
      :host {
        display: inline-block;
        border: 2px gray inset;
        background: #000;
        height: 80px;
      }
      #handle {
        background: gray;
        border: 5px gray outset;
        position: relative;
        left: 0;
        right: -25px;
      }
    `,
  ];

  @property() value: number = 0;

  @query("#handle") handleEl!: HTMLElement;

  troughHeight = 80 - 2 - 2 - 5 - 5;
  handleHeight = 10;

  drag?: Drag;
  unmutedValue: number = 1;

  render() {
    return html` <div id="handle"><hr /></div> `;
  }

  protected update(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
    super.update(changedProperties);
    if (changedProperties.has("value")) {
      
    }
  }
  valueChangedFromUi() {
    this.value= clamp(this.value, 0, 1)
    this.dispatchEvent(new CustomEvent("change", { detail: { value: this.value } }));
  }

  protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
    super.updated(_changedProperties);
    const y = this.sliderTopY(this.value);
    this.handleEl.style.top = y + "px";
  }

  protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
    super.firstUpdated(_changedProperties);
    this.handleEl.style.height = this.handleHeight + "px";
    this.events();
  }

  events() {
    const hand = this.handleEl;
    hand.addEventListener("mousedown", (ev: MouseEvent) => {
      ev.stopPropagation();
      if (ev.buttons == 1) {
        this.drag = new Drag(ev.clientY, this.value);
      } else if (ev.buttons == 2) {
        this.onRmb();
      }
    });
    this.addEventListener("mousedown", (ev: MouseEvent) => {
      ev.stopPropagation();
      if (ev.buttons == 1) {
        this.value = this.sliderValue(ev.offsetY);
        this.valueChangedFromUi()
        this.drag = new Drag(ev.clientY, this.value);
      } else if (ev.buttons == 2) {
        // RMB in trough
        this.onRmb();
      }
    });

    this.addEventListener("contextmenu", (event) => {
      event.preventDefault();
    });

    this.addEventListener("wheel", (ev: WheelEvent) => {
      ev.preventDefault();
      this.value += ev.deltaY / this.troughHeight * -.05;
      this.valueChangedFromUi()
    });

    const maybeDrag = (ev: MouseEvent) => {
      if (ev.buttons != 1) return;
      if (this.drag === undefined) return;
      ev.stopPropagation();
      this.onMouseDrag(ev.clientY - this.drag.startDragPxY!);
    };
    hand.addEventListener("mousemove", maybeDrag);
    this.addEventListener("mousemove", maybeDrag);
    window.addEventListener("mousemove", maybeDrag);

    hand.addEventListener("mouseup", this.onMouseUpAnywhere.bind(this));
    this.addEventListener("mouseup", this.onMouseUpAnywhere.bind(this));
    window.addEventListener("mouseup", this.onMouseUpAnywhere.bind(this));
  }
  onRmb() {
    if (this.value > 0.1) {
      // mute
      this.unmutedValue = this.value;
      this.value = 0;
    } else {
      // unmute
      this.value = this.unmutedValue;
    }
    this.valueChangedFromUi()
  }
  onMouseDrag(dy: number) {
    if (this.drag === undefined) throw "unexpected";
    this.value = this.drag.startDragValue - dy / this.troughHeight;
    this.valueChangedFromUi()
  }

  onMouseUpAnywhere() {
    this.drag = undefined;
  }

  sliderTopY(value: number): number {
    const usableY = this.troughHeight - this.handleHeight;
    const yAdj = this.handleHeight / 2 - 5 - 2;
    return (1 - value) * usableY + yAdj;
  }
  sliderValue(offsetY: number): number {
    const usableY = this.troughHeight - this.handleHeight;
    const yAdj = this.handleHeight / 2 - 5 - 2;
    return clamp(1 - (offsetY - yAdj) / usableY, 0, 1);
  }
}