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`
effect DeviceSettings
${this.devices.map(
(device: NamedNode) => html`
.graphToControls={this.graphToControls}
`
)}
`;
}
devices: Array = [];
@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) {
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()
}
}