Mercurial > code > home > repos > light9
diff web/live/Light9DeviceSettings.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/Light9DeviceSettings.ts@06bf6dae8e64 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/live/Light9DeviceSettings.ts Sun May 12 19:02:10 2024 -0700 @@ -0,0 +1,153 @@ +import debug from "debug"; +import { css, html, LitElement, PropertyValues } from "lit"; +import { customElement, property } 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 { Effect, newEffect } from "./Effect"; +export { EditChoice } from "../EditChoice"; +export { Light9DeviceControl as Light9LiveDeviceControl } from "./Light9DeviceControl"; +const log = debug("settings"); + +@customElement("light9-device-settings") +export class Light9DeviceSettings extends LitElement { + graph!: SyncedGraph; + + static styles = [ + css` + :host { + display: flex; + flex-direction: column; + } + #preview { + width: 100%; + } + #deviceControls { + flex-grow: 1; + position: relative; + width: 100%; + overflow-y: auto; + } + + light9-device-control > div { + break-inside: avoid-column; + } + light9-device-control { + vertical-align: top; + } + `, + ]; + + render() { + return html` + <rdfdb-synced-graph></rdfdb-synced-graph> + + <h1>effect DeviceSettings</h1> + + <div id="save"> + <div> + <button @click=${this.newEffect}>New effect</button> + <edit-choice .uri=${this.currentEffect ? this.currentEffect.uri : null} @edited=${this.onEffectChoice2} rename></edit-choice> + <button @click=${this.clearAll}>clear settings in this effect</button> + </div> + </div> + + <div id="deviceControls"> + ${this.devices.map( + (device: NamedNode) => html` + <light9-device-control .uri=${device} .effect=${this.currentEffect}> .graphToControls={this.graphToControls} </light9-device-control> + ` + )} + </div> + `; + } + + devices: Array<NamedNode> = []; + @property() currentEffect: Effect | null = null; + okToWriteUrl: boolean = false; + + constructor() { + super(); + + getTopGraph().then((g) => { + this.graph = g; + this.graph.runHandler(this.compile.bind(this), "findDevices"); + this.setEffectFromUrl(); + }); + } + + onEffectChoice2(ev: CustomEvent) { + const uri = ev.detail.newValue as NamedNode; + this.setCurrentEffect(uri); + } + setCurrentEffect(uri: NamedNode) { + if (uri === null) { + this.currentEffect = null; + // todo: wipe the UI settings + } else { + this.currentEffect = new Effect(this.graph, uri); + } + } + + updated(changedProperties: PropertyValues<this>) { + log("ctls udpated", changedProperties); + if (changedProperties.has("currentEffect")) { + log(`effectChoice to ${this.currentEffect?.uri?.value}`); + this.writeToUrl(this.currentEffect?.uri); + } + // this.graphToControls?.debugDump(); + } + + // Note that this doesn't fetch setting values, so it only should get rerun + // upon (rarer) changes to the devices etc. todo: make that be true + private compile(patch?: Patch) { + const U = this.graph.U(); + // if (patch && !patchContainsPreds(patch, [U("rdf:type")])) { + // return; + // } + + this.devices = []; + let classes = this.graph.subjects(U("rdf:type"), U(":DeviceClass")); + log(`found ${classes.length} device classes`); + 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(); + } + + 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) { + this.currentEffect = new Effect(this.graph, this.graph.Uri(effect)); + } + this.okToWriteUrl = true; + } + + writeToUrl(effect: NamedNode | undefined) { + const effectStr = effect ? this.graph.shorten(effect) : ""; + if (!this.okToWriteUrl) { + return; + } + const u = new URL(window.location.href); + if ((u.searchParams.get("effect") || "") === effectStr) { + return; + } + u.searchParams.set("effect", effectStr); // this escapes : and / and i wish it didn't + window.history.replaceState({}, "", u.href); + log("wrote new url", u.href); + } + + newEffect() { + this.setCurrentEffect(newEffect(this.graph)); + } + + clearAll() { + this.currentEffect?.clearAllSettings() + } +}