Files @ 69ca2b2fc133
Branch filter:

Location: light9/web/fade/Light9EffectFader.ts

drewp@bigasterisk.com
overcomplicated attempt at persisting the pane layout in the rdf graph

this was hard because we have to somehow wait for the graph to load before config'ing the panes
import debug from "debug";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { NamedNode, Quad } from "n3";
import { getTopGraph } from "../RdfdbSyncedGraph";
import { showRoot } from "../show_specific";
import { SyncedGraph } from "../SyncedGraph";
import { Patch } from "../patch";
import { Literal } from "n3";
export { Light9Fader } from "./Light9Fader";

const log = debug("efffader")

//////////////////////////////////////
const RETURN_URI = new NamedNode("");
const RETURN_FLOAT = 1;
function get2Step<T extends NamedNode | number>(returnWhat: T, graph: SyncedGraph, subj1: NamedNode, pred1: NamedNode, pred2: NamedNode): T | undefined {
  // ?subj1 ?pred1 ?x . ?x ?pred2 ?returned .
  let x: NamedNode;
  try {
    x = graph.uriValue(subj1, pred1);
  } catch (e) {
    return undefined;
  }
  try {
    if (typeof returnWhat === "object" && (returnWhat as NamedNode).termType == "NamedNode") {
      return graph.uriValue(x, pred2) as T;
    } else if (typeof returnWhat === "number") {
      return graph.floatValue(x, pred2) as T;
    }
  } catch (e) {
    return undefined;
  }
}
function set2Step(
  graph: SyncedGraph, //
  subj1: NamedNode,
  pred1: NamedNode,
  baseName: string,
  pred2: NamedNode,
  newObjLiteral: Literal
) { }

function maybeUriValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): NamedNode | undefined {
  try {
    return graph.uriValue(s, p);
  } catch (e) {
    return undefined;
  }
}
function maybeStringValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): string | undefined {
  try {
    return graph.stringValue(s, p);
  } catch (e) {
    return undefined;
  }
}
function maybeFloatValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): number | undefined {
  try {
    return graph.floatValue(s, p);
  } catch (e) {
    return undefined;
  }
}

//////////////////////////////////////
class EffectFader {
  constructor(public uri: NamedNode) { }
  column: string = "unset";
  effect?: NamedNode;
  effectAttr?: NamedNode; // :strength
  setting?: NamedNode; // we assume fader always has exactly one setting
  value?: number;
}

@customElement("light9-effect-fader")
export class Light9EffectFader extends LitElement {
  static styles = [
    css`
      :host {
        display: inline-block;
        border: 2px gray outset;
        background: #272727;
      }
      light9-fader {
        margin: 0px;
        width: 100%;
      }
    `,
  ];
  render() {
    if (this.conf === undefined || this.conf.value === undefined) {
      return html`...`;
    }
    return html`
      <div><resource-display .uri=${this.uri}></resource-display>
      <light9-fader .value=${this.conf.value} @change=${this.onSliderInput}></light9-fader>
      <div>${this.conf.value.toPrecision(3)}</div>
      <div>effect <edit-choice nounlink .uri=${this.conf.effect} @edited=${this.onEffectChange}></edit-choice></div>
      <div>attr <edit-choice nounlink .uri=${this.conf.effectAttr} @edited=${this.onEffectAttrChange}></edit-choice></div>
    `;
  }

  graph?: SyncedGraph;
  ctx: NamedNode = new NamedNode(showRoot + "/fade");
  @property() uri!: NamedNode;
  @state() conf?: EffectFader; // compiled from graph

  constructor() {
    super();
    getTopGraph().then((g) => {
      this.graph = g;
      this.graph.runHandler(this.compile.bind(this, this.graph), `fader config ${this.uri.value}`);
    });
  }

  private compile(graph: SyncedGraph) {
    const U = graph.U();
    this.conf = undefined;

    const conf = new EffectFader(this.uri);

    if (!graph.contains(this.uri, U("rdf:type"), U(":Fader"))) {
      // not loaded yet, perhaps
      return;
    }

    conf.column = maybeStringValue(graph, this.uri, U(":column")) || "unset";
    conf.effect = maybeUriValue(graph, this.uri, U(":effect"));
    conf.effectAttr = get2Step(RETURN_URI, graph, this.uri, U(":setting"), U(":effectAttr"));

    this.conf = conf;
    graph.runHandler(this.compileValue.bind(this, graph, this.conf), `fader config.value ${this.uri.value}`);
  }

  private compileValue(graph: SyncedGraph, conf: EffectFader) {
    // external graph change -> conf.value
    const U = graph.U();
    conf.value = get2Step(RETURN_FLOAT, graph, this.uri, U(":setting"), U(":value"));
    // since conf attrs aren't watched as property:
    this.requestUpdate()
  }

  onSliderInput(ev: CustomEvent) {
    // slider user input -> graph
    if (this.conf === undefined) return;
    this.conf.value = ev.detail.value
    this.writeValueToGraph()
  }

  writeValueToGraph() {
    // this.value -> graph
    if (this.graph === undefined) {
      return;
    }
    const U = this.graph.U();
    if (this.conf === undefined) {
      return;
    }
    if (this.conf.value === undefined) {
      log(`value of ${this.uri} is undefined`)
      return;
    }
    log('writeValueToGraph', this.conf.value)
    const valueTerm = this.graph.LiteralRoundedFloat(this.conf.value);
    const settingNode = this.graph.uriValue(this.uri, U(":setting"));
    this.graph.patchObject(settingNode, this.graph.Uri(":value"), valueTerm, this.ctx);

  }

  onEffectChange(ev: CustomEvent) {
    if (this.graph === undefined) {
      return;
    }
    const { newValue } = ev.detail;
    this.graph.patchObject(this.uri, this.graph.Uri(":effect"), newValue, this.ctx);
  }

  onEffectAttrChange(ev: CustomEvent) {
    if (this.graph === undefined) {
      return;
    }
    // const { newValue } = ev.detail;
    // if (this.setting === undefined) {
    //   this.setting = this.graph.nextNumberedResource(this.graph.Uri(":fade_set"));
    //   this.graph.patchObject(this.uri, this.graph.Uri(":setting"), this.setting, this.ctx);
    // }
    // this.graph.patchObject(this.setting, this.graph.Uri(":effectAttr"), newValue, this.ctx);
  }
}