Files @ 9cbc93f80b05
Branch filter:

Location: light9/web/live/Light9DeviceControl.ts - annotation

drewp@bigasterisk.com
cleanup
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
import debug from "debug";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
import { NamedNode } from "n3";
import { unique } from "underscore";
import { Patch } from "../patch";
import { getTopGraph } from "../RdfdbSyncedGraph";
import { SyncedGraph } from "../SyncedGraph";
import { Choice } from "./Light9Listbox";
import { Light9AttrControl } from "./Light9AttrControl";
import { Effect } from "./Effect";
export { ResourceDisplay } from "../ResourceDisplay";
export { Light9AttrControl };
const log = debug("settings.dev");

export interface DeviceAttrRow {
  uri: NamedNode; //devattr
  device: NamedNode;
  attrClasses: string; // the css kind
  dataType: NamedNode;
  choices: Choice[];
  // choiceSize: number;
  // max: number;
}

// Widgets for one device with multiple Light9LiveControl rows for the attr(s).
@customElement("light9-device-control")
export class Light9DeviceControl extends LitElement {
  graph!: SyncedGraph;
  static styles = [
    css`
      :host {
        display: inline-block;
      }
      .device {
        border: 2px solid #151e2d;
        margin: 4px;
        padding: 1px;
        background: #171717; /* deviceClass gradient added later */
        break-inside: avoid-column;
        width: 335px;
      }
      .deviceAttr {
        border-top: 1px solid #272727;
        padding-bottom: 2px;
        display: flex;
      }
      .deviceAttr > span {
      }
      .deviceAttr > light9-live-control {
        flex-grow: 1;
      }
      h2 {
        font-size: 110%;
        padding: 4px;
        margin-top: 0;
        margin-bottom: 0;
      }
      .device,
      h2 {
        border-top-right-radius: 15px;
      }

      #mainLabel {
        font-size: 120%;
        color: #9ab8fd;
        text-decoration: initial;
      }
      .device.selected h2 {
        outline: 3px solid #ffff0047;
      }
      .deviceAttr.selected {
        background: #cada1829;
      }
    `,
  ];

  render() {
    return html`
      <div class="device ${this.devClasses}">
        <h2 style="${this._bgStyle(this.deviceClass)}" @click=${this.onClick}>
          <resource-display id="mainLabel" .uri="${this.uri}"></resource-display>
          a <resource-display minor .uri="${this.deviceClass}"></resource-display>
        </h2>

        ${this.deviceAttrs.map(
          (dattr: DeviceAttrRow) => html`
            <div @click="onAttrClick" class="deviceAttr ${dattr.attrClasses}">
              <span>
                attr
                <resource-display minor .uri=${dattr.uri}></resource-display>
              </span>
              <light9-attr-control .deviceAttrRow=${dattr} .effect=${this.effect}>
              </light9-attr-control>
            </div>
          `
        )}
      </div>
    `;
  }

  @property() uri!: NamedNode;
  @property() effect!: Effect;

  @property() devClasses: string = ""; // the css kind
  @property() deviceAttrs: DeviceAttrRow[] = [];
  @property() deviceClass: NamedNode | null = null;
  @property() selectedAttrs: Set<NamedNode> = new Set();

  constructor() {
    super();
    getTopGraph().then((g) => {
      this.graph = g;
      this.graph.runHandler(this.syncDeviceAttrsFromGraph.bind(this), `${this.uri.value} update`);
    });
    this.selectedAttrs = new Set();
  }

  _bgStyle(deviceClass: NamedNode | null): string {
    if (!deviceClass) return "";
    let hash = 0;
    const u = deviceClass.value;
    for (let i = u.length - 10; i < u.length; i++) {
      hash += u.charCodeAt(i);
    }
    const hue = (hash * 8) % 360;
    const accent = `hsl(${hue}, 49%, 22%)`;
    return `background: linear-gradient(to right, rgba(31,31,31,0) 50%, ${accent} 100%);`;
  }

  setDeviceSelected(isSel: any) {
    this.devClasses = isSel ? "selected" : "";
  }

  setAttrSelected(devAttr: NamedNode, isSel: boolean) {
    if (isSel) {
      this.selectedAttrs.add(devAttr);
    } else {
      this.selectedAttrs.delete(devAttr);
    }
  }

  syncDeviceAttrsFromGraph(patch?: Patch) {
    const U = this.graph.U();
    if (patch && !patch.containsAnyPreds([U("rdf:type"), U(":deviceAttr"), U(":dataType"), U(":choice")])) {
      return;
    }
    try {
      this.deviceClass = this.graph.uriValue(this.uri, U("rdf:type"));
    } catch (e) {
      // what's likely is we're going through a graph reload and the graph
      // is gone but the controls remain
    }
    this.deviceAttrs = [];
    Array.from(unique(this.graph.sortedUris(this.graph.objects(this.deviceClass, U(":deviceAttr"))))).map((da: NamedNode) =>
      this.deviceAttrs.push(this.attrRow(da))
    );
    this.requestUpdate();
  }

  attrRow(devAttr: NamedNode): DeviceAttrRow {
    let x: NamedNode;
    const U = (x: string) => this.graph.Uri(x);
    const dataType = this.graph.uriValue(devAttr, U(":dataType"));
    const daRow = {
      uri: devAttr,
      device: this.uri,
      dataType,
      attrClasses: this.selectedAttrs.has(devAttr) ? "selected" : "",
      choices: [] as Choice[],
      choiceSize: 0,
      max: 1,
    };
     if (dataType.equals(U(":choice"))) {
      const choiceUris = this.graph.sortedUris(this.graph.objects(devAttr, U(":choice")));
      daRow.choices = (() => {
        const result = [];
        for (x of Array.from(choiceUris)) {
          result.push({ uri: x.value, label: this.graph.labelOrTail(x) });
        }
        return result;
      })();
      daRow.choiceSize = Math.min(choiceUris.length + 1, 10);
    } else {
      daRow.max = 1;
      if (dataType.equals(U(":angle"))) {
        // varies
        daRow.max = 1;
      }
    }
    return daRow;
  }

  clear() {
    // why can't we just set their values ? what's diff about
    // the clear state, and should it be represented with `null` value?
    throw new Error();
    // Array.from(this.shadowRoot!.querySelectorAll("light9-live-control")).map((lc: Element) => (lc as Light9LiveControl).clear());
  }

  onClick(ev: any) {
    log("click", this.uri);
    // select, etc
  }

  onAttrClick(ev: { model: { dattr: { uri: any } } }) {
    log("attr click", this.uri, ev.model.dattr.uri);
    // select
  }
}