# HG changeset patch
# User drewp@bigasterisk.com
# Date 1653544715 25200
# Node ID c4eab47d3c839478558c7e909415e1ee86556618
# Parent ad7ab7027907fe1fe643562e436b4a26de525054
WIP half-ported live/ page to working TS
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/GraphToControls.ts
--- a/light9/web/live/GraphToControls.ts Wed May 25 01:11:41 2022 -0700
+++ b/light9/web/live/GraphToControls.ts Wed May 25 22:58:35 2022 -0700
@@ -67,12 +67,12 @@
return this.graph.Uri(effect.value.replace("light9.bigasterisk.com/effect", "light9.bigasterisk.com/show/dance2019/effect"));
}
- setEffect(effect: NamedNode) {
+ setEffect(effect: NamedNode | null) {
this.clearSettings();
this.effect = effect;
- this.ctx = this.ctxForEffect(effect);
+ this.ctx = !effect ? null : this.ctxForEffect(effect);
// are these going to pile up? consider @graph.triggerHandler('GTC sync')
- return this.graph.runHandler(this.syncFromGraph.bind(this), "GraphToControls sync");
+ this.graph.runHandler(this.syncFromGraph.bind(this), "GraphToControls sync");
}
newEffect() {
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/Light9DeviceControl.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/web/live/Light9DeviceControl.ts Wed May 25 22:58:35 2022 -0700
@@ -0,0 +1,213 @@
+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, patchContainsPreds } from "../patch";
+import { getTopGraph } from "../RdfdbSyncedGraph";
+import { SyncedGraph } from "../SyncedGraph";
+import { GraphToControls } from "./GraphToControls";
+import { Choice } from "./Light9Listbox";
+import { Light9LiveControl } from "./Light9LiveControl";
+export { ResourceDisplay } from "../ResourceDisplay";
+export { Light9LiveControl };
+const log = debug("devcontrol");
+
+interface DeviceAttrRow {
+ uri: NamedNode; //devattr
+ attrClasses: string; // the css kind
+ dataType: NamedNode;
+ showColorPicker: boolean;
+ useColor: boolean;
+ useChoice: boolean;
+ choices: Choice[];
+ choiceSize: number;
+ useSlider: boolean;
+ max: number;
+}
+
+@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`
+
+
+
+ a
+
+ ${this.deviceAttrs.map(
+ (dattr: DeviceAttrRow) => html`
+
+ attr
+
+
+ `
+ )}
+
+ `;
+ }
+
+ @property() uri!: NamedNode;
+ @property() effect!: NamedNode;
+ @property() graphToControls!: GraphToControls;
+
+ @property() devClasses: string = ""; // the css kind
+ @property() deviceAttrs: DeviceAttrRow[] = [];
+ @property() deviceClass: NamedNode | null = null;
+ @property() selectedAttrs: Set = new Set();
+
+ constructor() {
+ super();
+ getTopGraph().then((g) => {
+ this.graph = g;
+ this.graph.runHandler(this.configureFromGraphz.bind(this), `${this.uri.value} update`);
+ });
+ this.selectedAttrs = new Set(); // uri strings
+ }
+
+ _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.value);
+ } else {
+ this.selectedAttrs.delete(devAttr.value);
+ }
+ return this.configureFromGraphz();
+ }
+
+ configureFromGraphz(patch?: Patch) {
+ const U = this.graph.U();
+ if (patch != null && !patchContainsPreds(patch, [U("rdf:type"), U(":deviceAttr"), U(":dataType"), U(":choice")])) {
+ return;
+ }
+ this.deviceClass = this.graph.uriValue(this.uri, U("rdf:type"));
+ 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,
+ dataType,
+ showColorPicker: dataType.equals(U(":color")),
+ attrClasses: this.selectedAttrs.has(devAttr.value) ? "selected" : "",
+ useColor: false,
+ useChoice: false,
+ choices: [] as Choice[],
+ choiceSize: 0,
+ useSlider: false,
+ max: 1,
+ };
+ if (dataType.equals(U(":color"))) {
+ daRow.useColor = true;
+ } else if (dataType.equals(U(":choice"))) {
+ daRow.useChoice = true;
+ 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.useSlider = true;
+ daRow.max = 1;
+ if (dataType.equals(U(":angle"))) {
+ // varies
+ daRow.max = 1;
+ }
+ }
+ return daRow;
+ }
+
+ clear() {
+ 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
+ }
+}
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/Light9Listbox.ts
--- a/light9/web/live/Light9Listbox.ts Wed May 25 01:11:41 2022 -0700
+++ b/light9/web/live/Light9Listbox.ts Wed May 25 22:58:35 2022 -0700
@@ -1,8 +1,8 @@
import debug from "debug";
const log = debug("listbox");
-import { css, html, LitElement } from "lit";
+import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
-
+export type Choice = {uri:string,label:string}
@customElement("light9-listbox")
export class Light9Listbox extends LitElement {
static styles = [
@@ -40,11 +40,12 @@
`;
}
- properties: {
- choices: { type: Array };
- value: { type: String; notify: true };
- };
- observers: ["onValue(value)"];
+ @property() choices: Array = [];
+ @property() value: String | null = null;
+
+ constructor() {
+ super();
+ }
selectOnFocus(ev) {
if (ev.target.uri === undefined) {
// *don't* clear for this, or we can't cycle through all choices (including none) with up/down keys
@@ -53,19 +54,22 @@
}
this.value = ev.target.uri;
}
- onValue(value) {
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has("value")) {
+ if (this.value === null) {
+ this.clear();
+ }
+ }
+ }
+ onValue(value: String | null) {
if (value === null) {
this.clear();
}
}
clear() {
- this.async(
- function () {
- this.querySelectorAll("paper-item").forEach(function (item) {
- item.blur();
- });
- this.value = undefined;
- }.bind(this)
- );
+ this.querySelectorAll("paper-item").forEach(function (item) {
+ item.blur();
+ });
+ this.value = null;
}
}
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/Light9LiveControl.ts
--- a/light9/web/live/Light9LiveControl.ts Wed May 25 01:11:41 2022 -0700
+++ b/light9/web/live/Light9LiveControl.ts Wed May 25 22:58:35 2022 -0700
@@ -2,9 +2,12 @@
const log = debug("control");
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
+import { SyncedGraph } from "../SyncedGraph";
@customElement("light9-live-control")
export class Light9LiveControl extends LitElement {
+ graph!:SyncedGraph
+
static styles = [
css`
#colorControls {
@@ -61,41 +64,38 @@
`;
}
- graph: { type: any; notify: boolean; }
- device: { type: any; };
- deviceAttrRow: { type: any; }; // object returned from attrRow, below
- value: { type: any; notify: boolean; }; // null, Uri, float, str
- choiceValue: { type: any; };
- immediateSlider: { notify: boolean; observer: string; };
- sliderWriteValue: { ...; };
- pickedChoice: { ...; };
- graphToControls: { ...; };
+
+
- enableChange: boolean;
- value: any;
- immediateSlider: any;
- deviceAttrRow: any;
- sliderWriteValue: { value: any; };
- choiceValue: any;
- graphToControls: any;
- graph: any;
- pickedChoice: any;
- static initClass() {
- this.getter_properties = {
- graph: { type: Object, notify: true },
- device: { type: Object },
- deviceAttrRow: { type: Object }, // object returned from attrRow, below
- value: { type: Object, notify: true }, // null, Uri, float, str
- choiceValue: { type: Object },
-
- immediateSlider: { notify: true, observer: "onSlider" },
- sliderWriteValue: { type: Number },
-
- pickedChoice: { observer: "onChange" },
- graphToControls: { type: Object },
- };
- this.getter_observers = ["onChange(value)", "onGraphToControls(graphToControls)", "onChoice(choiceValue)"];
- }
+ // "onChange(value)",
+ // "onChoice(choiceValue)"];
+ // "onGraphToControls(graphToControls)",
+ // choiceValue: { type: any; };
+ // choiceValue: { type: Object },
+ // choiceValue: any;
+ // device: { type: any; };
+ // device: { type: Object },
+ // deviceAttrRow: { type: any; }; // object returned from attrRow, below
+ // deviceAttrRow: { type: Object }, // object returned from attrRow, below
+ // deviceAttrRow: any;
+ // enableChange: boolean;
+ // graph: { type: Object, notify: true },
+ // graphToControls: { ...; };
+ // graphToControls: { type: Object },
+ // graphToControls: any;
+ // immediateSlider: { notify: boolean; observer: string; };
+ // immediateSlider: { notify: true, observer: "onSlider" },
+ // immediateSlider: any;
+ // pickedChoice: { ...; };
+ // pickedChoice: { observer: "onChange" },
+ // pickedChoice: any;
+ // sliderWriteValue: { ...; };
+ // sliderWriteValue: { type: Number },
+ // sliderWriteValue: { value: any; };
+ // value: { type: any; notify: boolean; }; // null, Uri, float, str
+ // value: { type: Object, notify: true }, // null, Uri, float, str
+ // value: any;
+
constructor() {
super();
this.enableChange = false; // until 1st graph read
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/Light9LiveControls.ts
--- a/light9/web/live/Light9LiveControls.ts Wed May 25 01:11:41 2022 -0700
+++ b/light9/web/live/Light9LiveControls.ts Wed May 25 22:58:35 2022 -0700
@@ -1,11 +1,20 @@
import debug from "debug";
-const log = debug("controls");
import { css, html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators.js";
+import { customElement } from "lit/decorators.js";
+import { NamedNode } from "n3";
+import { sortBy, uniq } from "underscore";
+import { Patch } from "../patch";
+import { getTopGraph } from "../RdfdbSyncedGraph";
+import { SyncedGraph } from "../SyncedGraph";
import { GraphToControls } from "./GraphToControls";
+export { Light9DeviceControl as Light9LiveDeviceControl } from "./Light9DeviceControl";
+
+const log = debug("controls");
@customElement("light9-live-controls")
export class Light9LiveControls extends LitElement {
+ graph!: SyncedGraph;
+
static styles = [
css`
:host {
@@ -32,85 +41,67 @@
render() {
return html`
-
+
device control
-
+
-
-
-
+ ${this.devices.map(
+ (device: NamedNode) => html`
+
+ `
+ )}
`;
}
- static getter_properties: {
- graph: { type: any; notify: boolean };
- devices: { type: any; notify: boolean; value: {} };
- // string uri of the effect being edited, or null. This is the
- // master value; GraphToControls follows.
- effectChoice: { type: any; notify: boolean; value: any };
- graphToControls: { type: any };
- };
- static getter_observers: {};
- graphToControls: any;
- okToWriteUrl: boolean;
- currentSettings: {};
- graph: any;
- effectChoice: any;
- static initClass() {
- this.getter_properties = {
- graph: { type: Object, notify: true },
- devices: { type: Array, notify: true, value: [] },
- // string uri of the effect being edited, or null. This is the
- // master value; GraphToControls follows.
- effectChoice: { type: String, notify: true, value: null },
- graphToControls: { type: Object },
- };
- this.getter_observers = ["onGraph(graph)", "onEffectChoice(effectChoice)"];
- }
+ devices: Array = [];
+ // uri of the effect being edited, or null. This is the
+ // master value; GraphToControls follows.
+ effectChoice: NamedNode | null = null;
+ graphToControls!: GraphToControls;
+ okToWriteUrl: boolean = false;
constructor() {
super();
- this.graphToControls = null;
- this.okToWriteUrl = false;
- }
-
- ready() {
- super.ready(...arguments).ready();
- return (this.currentSettings = {});
+ getTopGraph().then((g) => {
+ this.graph = g;
+ this.graph.runHandler(this.findDevices.bind(this), "findDevices");
+ this.graphToControls = new GraphToControls(this.graph);
+ // this.graph.runHandler(this.xupdate.bind(this), "Light9LiveControls update");
+ this.setEffectFromUrl.bind(this);
+ });
}
- onGraph() {
- this.graphToControls = new GraphToControls(this.graph);
- this.graph.runHandler(this.update.bind(this), "Light9LiveControls update");
+ findDevices(patch?: Patch) {
+ const U = this.graph.U();
- // need graph to be loaded, so we don't make double settings? not sure.
- return setTimeout(this.setFromUrl.bind(this), 1);
+ this.devices = [];
+ let classes = this.graph.subjects(U("rdf:type"), U(":DeviceClass"));
+ uniq(sortBy(classes, "value"), true).forEach((dc) => {
+ sortBy(this.graph.subjects(U("rdf:type"), dc), "value").forEach((dev) => {
+ this.devices.push(dev as NamedNode);
+ });
+ });
+ this.requestUpdate();
}
-
- setFromUrl() {
+ setEffectFromUrl() {
// not a continuous bidi link between url and effect; it only reads
// the url when the page loads.
const effect = new URL(window.location.href).searchParams.get("effect");
if (effect != null) {
log("found url", effect);
- this.effectChoice = effect;
+ this.effectChoice = this.graph.Uri(effect);
}
- return (this.okToWriteUrl = true);
+ this.okToWriteUrl = true;
}
writeToUrl(effectStr: any) {
@@ -140,7 +131,7 @@
} else {
log("load", this.effectChoice);
if (this.graphToControls != null) {
- this.graphToControls.setEffect(this.graph.Uri(this.effectChoice));
+ this.graphToControls.setEffect(this.effectChoice);
}
}
return this.writeToUrl(this.effectChoice);
@@ -151,7 +142,7 @@
return this.graphToControls.emptyEffect();
}
- update() {
+ configureFromGraph() {
const U = (x: string) => this.graph.Uri(x);
const newDevs = [];
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/Light9LiveDeviceControl.ts
--- a/light9/web/live/Light9LiveDeviceControl.ts Wed May 25 01:11:41 2022 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-import debug from "debug";
-const log = debug("devcontrol");
-import { css, html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators.js";
-
-@customElement("light9-device-control")
-export class Light9DeviceControl extends LitElement {
- 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`
-
-
-
- a
-
-
-
- attr
-
-
-
-
- `;
- }
-
- static getter_properties: {
- graph: { type: any; notify: boolean; }; uri: { type: any; notify: boolean; }; effect: { type: any; }; deviceClass: { type: any; notify: boolean; }; // the uri str
- deviceAttrs: { type: any; notify: boolean; }; graphToControls: { ...; }; bgStyle: { ...; }; devClasses: { ...; }; // the css kind
- };
- selectedAttrs: any;
- graph: any;
- uri: any;
- devClasses: string;
- deviceClass: any;
- deviceAttrs: {};
- shadowRoot: any;
- static initClass() {
- this.getter_properties = {
- graph: { type: Object, notify: true },
- uri: { type: String, notify: true },
- effect: { type: String },
- deviceClass: { type: String, notify: true }, // the uri str
- deviceAttrs: { type: Array, notify: true },
- graphToControls: { type: Object },
- bgStyle: { type: String, computed: "_bgStyle(deviceClass)" },
- devClasses: { type: String, value: "" }, // the css kind
- };
- this.getter_observers = ["onGraph(graph)"];
- }
- constructor() {
- super();
- this.selectedAttrs = new Set(); // uri strings
- }
- _bgStyle(deviceClass: { value: any; length: number; charCodeAt: (arg0: number) => number }) {
- let hash = 0;
- deviceClass = deviceClass.value;
- for (let start = deviceClass.length - 10, i = start, end = deviceClass.length, asc = start <= end; asc ? i < end : i > end; asc ? i++ : i--) {
- hash += deviceClass.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%);`;
- }
-
- onGraph() {
- return this.graph.runHandler(this.update.bind(this), `${this.uri.value} update`);
- }
-
- setDeviceSelected(isSel: any) {
- return (this.devClasses = isSel ? "selected" : "");
- }
-
- setAttrSelected(devAttr: { value: any }, isSel: any) {
- if (isSel) {
- this.selectedAttrs.add(devAttr.value);
- } else {
- this.selectedAttrs.delete(devAttr.value);
- }
- return this.update();
- }
-
- update(patch: null) {
- const U = (x: string) => this.graph.Uri(x);
- if (patch != null && !SyncedGraph.patchContainsPreds(patch, [U("rdf:type"), U(":deviceAttr"), U(":dataType"), U(":choice")])) {
- return;
- }
- this.deviceClass = this.graph.uriValue(this.uri, U("rdf:type"));
- this.deviceAttrs = [];
- return Array.from(_.unique(this.graph.sortedUris(this.graph.objects(this.deviceClass, U(":deviceAttr"))))).map((da: any) =>
- this.push("deviceAttrs", this.attrRow(da))
- );
- }
- push(arg0: string, arg1: { uri: { value: any }; dataType: any; showColorPicker: any; attrClasses: string }) {
- throw new Error("Method not implemented.");
- }
-
- attrRow(devAttr: { value: any }) {
- let x: { value: any };
- const U = (x: string) => this.graph.Uri(x);
- const dataType = this.graph.uriValue(devAttr, U(":dataType"));
- const daRow = {
- uri: devAttr,
- dataType,
- showColorPicker: dataType.equals(U(":color")),
- attrClasses: this.selectedAttrs.has(devAttr.value) ? "selected" : "",
- };
- if (dataType.equals(U(":color"))) {
- daRow.useColor = true;
- } else if (dataType.equals(U(":choice"))) {
- daRow.useChoice = true;
- 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.useSlider = true;
- daRow.max = 1;
- if (dataType.equals(U(":angle"))) {
- // varies
- daRow.max = 1;
- }
- }
- return daRow;
- }
-
- clear() {
- return Array.from(this.shadowRoot.querySelectorAll("light9-live-control")).map((lc: { clear: () => any }) => lc.clear());
- }
-
- onClick(ev: any) {
- return log("click", this.uri);
- }
- // select, etc
-
- onAttrClick(ev: { model: { dattr: { uri: any } } }) {
- return log("attr click", this.uri, ev.model.dattr.uri);
- }
-}
-// select
diff -r ad7ab7027907 -r c4eab47d3c83 light9/web/live/index.html
--- a/light9/web/live/index.html Wed May 25 01:11:41 2022 -0700
+++ b/light9/web/live/index.html Wed May 25 22:58:35 2022 -0700
@@ -3,7 +3,8 @@
device control
-
+
+