Mercurial > code > home > repos > light9
diff web/live/Light9AttrControl.ts @ 2376:4556eebe5d73
topdir reorgs; let pdm have its src/ dir; separate vite area from light9/
author | drewp@bigasterisk.com |
---|---|
date | Sun, 12 May 2024 19:02:10 -0700 |
parents | light9/web/live/Light9AttrControl.ts@06bf6dae8e64 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/live/Light9AttrControl.ts Sun May 12 19:02:10 2024 -0700 @@ -0,0 +1,195 @@ +import debug from "debug"; +import { css, html, LitElement, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { Literal, NamedNode } from "n3"; +import { SubEvent } from "sub-events"; +import { getTopGraph } from "../RdfdbSyncedGraph"; +import { SyncedGraph } from "../SyncedGraph"; +import { ControlValue, Effect } from "./Effect"; +import { DeviceAttrRow } from "./Light9DeviceControl"; +export { Slider } from "@material/mwc-slider"; +export { Light9ColorPicker } from "../light9-color-picker"; +export { Light9Listbox } from "./Light9Listbox"; +const log = debug("settings.dev.attr"); + +type DataTypeNames = "scalar" | "color" | "choice"; +const makeType = (d: DataTypeNames) => new NamedNode(`http://light9.bigasterisk.com/${d}`); + +// UI for one device attr (of any type). +@customElement("light9-attr-control") +export class Light9AttrControl extends LitElement { + graph!: SyncedGraph; + + static styles = [ + css` + #colorControls { + display: flex; + align-items: center; + } + #colorControls > * { + margin: 0 3px; + } + :host { + } + mwc-slider { + width: 250px; + } + `, + ]; + + @property() deviceAttrRow: DeviceAttrRow | null = null; + @state() dataType: DataTypeNames = "scalar"; + @property() effect: Effect | null = null; + @property() enableChange: boolean = false; + @property() value: ControlValue | null = null; // e.g. color string + + constructor() { + super(); + getTopGraph().then((g) => { + this.graph = g; + if (this.deviceAttrRow === null) throw new Error(); + }); + } + + connectedCallback(): void { + super.connectedCallback(); + setTimeout(() => { + // only needed once per page layout + this.shadowRoot?.querySelector("mwc-slider")?.layout(/*skipUpdateUI=*/ false); + }, 1); + } + + render() { + if (this.deviceAttrRow === null) throw new Error(); + if (this.dataType == "scalar") { + const v = this.value || 0; + return html`<mwc-slider .value=${v} step=${1 / 255} min="0" max="1" @input=${this.onValueInput}></mwc-slider> `; + } else if ((this.dataType = "color")) { + const v = this.value || "#000"; + return html` + <div id="colorControls"> + <button @click=${this.goBlack}>0.0</button> + <light9-color-picker .color=${v} @input=${this.onValueInput}></light9-color-picker> + </div> + `; + } else if (this.dataType == "choice") { + return html`<light9-listbox .choices=${this.deviceAttrRow.choices} .value=${this.value}> </light9-listbox> `; + } + } + + updated(changedProperties: PropertyValues<this>) { + super.updated(changedProperties); + + if (changedProperties.has("deviceAttrRow")) { + this.onDeviceAttrRowProperty(); + } + if (changedProperties.has("effect")) { + this.onEffectProperty(); + } + if (changedProperties.has("value")) { + this.onValueProperty(); + } + } + + private onValueProperty() { + if (this.deviceAttrRow === null) throw new Error(); + if (!this.graph) { + log('ignoring value change- no graph yet') + return; + } + if (this.effect === null) { + this.value = null; + } else { + const p = this.effect.edit( + // + this.deviceAttrRow.device, + this.deviceAttrRow.uri, + this.value + ); + if (!p.isEmpty()) { + log("Effect told us to graph.patch this:\n", p.dump()); + this.graph.applyAndSendPatch(p); + } + } + } + + private onEffectProperty() { + if (this.effect === null) { + log('no effect obj yet') + return; + } + // effect will read graph changes on its own, but emit an event when it does + this.effect.settingsChanged.subscribe(() => { + this.effectSettingsChanged(); + }); + this.effectSettingsChanged(); + } + + private effectSettingsChanged() { + // something in the settings graph is new + if (this.deviceAttrRow === null) throw new Error(); + if (this.effect === null) throw new Error(); + // log("graph->ui on ", this.deviceAttrRow.device, this.deviceAttrRow.uri); + const v = this.effect.currentValue(this.deviceAttrRow.device, this.deviceAttrRow.uri); + this.onGraphValueChanged(v); + } + + private onDeviceAttrRowProperty() { + if (this.deviceAttrRow === null) throw new Error(); + const d = this.deviceAttrRow.dataType; + if (d.equals(makeType("scalar"))) { + this.dataType = "scalar"; + } else if (d.equals(makeType("color"))) { + this.dataType = "color"; + } else if (d.equals(makeType("choice"))) { + this.dataType = "choice"; + } + } + + onValueInput(ev: CustomEvent) { + if (ev.detail === undefined) { + // not sure what this is, but it seems to be followed by good events + return; + } + // log(ev.type, ev.detail.value); + this.value = ev.detail.value; + // this.graphToControls.controlChanged(this.device, this.deviceAttrRow.uri, ev.detail.value); + } + + onGraphValueChanged(v: ControlValue | null) { + if (this.deviceAttrRow === null) throw new Error(); + // log("change: control must display", v, "for", this.deviceAttrRow.device.value, this.deviceAttrRow.uri.value); + // this.enableChange = false; + if (this.dataType == "scalar") { + if (v !== null) { + this.value = v; + } else { + this.value = 0; + } + } else if (this.dataType == "color") { + this.value = v; + } + } + + goBlack() { + this.value = "#000000"; + } + + onChoice(value: any) { + // if (value != null) { + // value = this.graph.Uri(value); + // } else { + // value = null; + // } + } + + onChange(value: any) { + // if (typeof value === "number" && isNaN(value)) { + // return; + // } // let onChoice do it + // //log('change: control tells graph', @deviceAttrRow.uri.value, value) + // if (value === undefined) { + // value = null; + // } + } +}