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`
a
${this.deviceAttrs.map(
(dattr: DeviceAttrRow) => html`
attr
`
)}
`;
}
@property() uri!: NamedNode;
@property() effect!: Effect;
@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.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
}
}