diff web/ResourceDisplay.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/ResourceDisplay.ts@ed0db55f604c
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/ResourceDisplay.ts	Sun May 12 19:02:10 2024 -0700
@@ -0,0 +1,164 @@
+import { TextField } from "@material/mwc-textfield";
+import debug from "debug";
+import { css, html, LitElement, PropertyValues } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import { NamedNode } from "n3";
+import { getTopGraph } from "./RdfdbSyncedGraph";
+import { SyncedGraph } from "./SyncedGraph";
+export { Button } from "@material/mwc-button";
+export { Dialog } from "@material/mwc-dialog";
+export { TextField } from "@material/mwc-textfield";
+
+const log = debug("rdisplay");
+
+@customElement("resource-display")
+export class ResourceDisplay extends LitElement {
+  graph!: SyncedGraph;
+  static styles = [
+    css`
+      :host {
+        display: inline-block;
+      }
+
+      a.resource {
+        color: inherit;
+        text-decoration: none;
+      }
+
+      .resource {
+        border: 1px solid #545454;
+        border-radius: 5px;
+        padding: 1px;
+        margin: 2px;
+        background: rgb(49, 49, 49);
+        display: inline-block;
+        text-shadow: 1px 1px 2px black;
+      }
+      .resource.minor {
+        background: none;
+        border: none;
+      }
+      .resource a {
+        color: rgb(150, 150, 255);
+        padding: 1px;
+        display: inline-block;
+      }
+      .resource.minor a {
+        text-decoration: none;
+        color: rgb(155, 155, 193);
+        padding: 0;
+      }
+    `,
+  ];
+
+  render() {
+    let renameDialog = html``;
+    if (this.renameDialogOpen) {
+      renameDialog = html` <mwc-dialog id="renameDialog" open @closing=${this.onRenameClosing} @closed=${this.onRenameClosed}>
+        <p>
+          New label:
+          <mwc-textfield id="renameField" dialogInitialFocus .value=${this.renameTo}></mwc-textfield>
+        </p>
+        <mwc-button dialogAction="cancel" slot="secondaryAction">Cancel</mwc-button>
+        <mwc-button dialogAction="ok" slot="primaryAction">OK</mwc-button>
+      </mwc-dialog>`;
+    }
+
+    return html` <span class="${this.resClasses()}">
+        <a href="${this.href()}" id="uri"> <!-- type icon goes here -->${this.label}</a>
+        ${this.rename ? html`<button @click=${this.onRename}>Rename</button>` : ""} </span
+      >${renameDialog}`;
+    //
+  }
+  @property() uri?: NamedNode;
+
+  @state() label: string = "";
+  @state() renameDialogOpen = false;
+  @state() renameTo = "";
+
+  @property({ type: Boolean }) rename: boolean = false;
+  @property({ type: Boolean }) noclick: boolean = false;
+  @property({ type: Boolean }) minor: boolean = false;
+
+  constructor() {
+    super();
+    getTopGraph().then((g) => {
+      this.graph = g;
+      this.onUri();
+    });
+  }
+
+  updated(changedProperties: PropertyValues) {
+    if (changedProperties.has("uri")) {
+      this.onUri();
+    }
+  }
+
+  private onUri() {
+    if (!this.graph) {
+      return; /*too soon, but getTopGraph will call us again*/
+    }
+    
+    if (this.uri === undefined) {
+      this.label = "(unset)";
+    } else if  (this.uri === null) {
+      throw 'use undefined please'
+    } else {
+      this.graph.runHandler(this.compile.bind(this, this.graph), `label for ${this.uri.id}`);
+    }
+  }
+  private compile(graph: SyncedGraph) {
+    if (this.uri === undefined) {
+      return;
+    } else {
+      this.label = this.graph.labelOrTail(this.uri);
+    }
+  }
+
+  private href(): string {
+    if (!this.uri || this.noclick) {
+      return "javascript:;";
+    }
+    return this.uri.value;
+  }
+
+  private resClasses() {
+    return this.minor ? "resource minor" : "resource";
+  }
+
+  private onRename() {
+    this.renameTo = this.label;
+    this.renameDialogOpen = true;
+    setTimeout(() => {
+      // I! 👏 know! 👏 the! 👏 element! 👏 I! 👏 want!
+      const inputEl = this.shadowRoot!.querySelector("#renameField")!.shadowRoot!.querySelector("input")! as HTMLInputElement;
+      inputEl.setSelectionRange(0, -1);
+    }, 100);
+  }
+
+  // move to SyncedGraph
+  private whatCtxHeldTheObj(subj: NamedNode, pred: NamedNode): NamedNode {
+    var ctxs = this.graph.contextsWithPattern(subj, pred, null);
+    if (ctxs.length != 1) {
+      throw new Error(`${ctxs.length} ${pred.id} stmts for ${subj.id}`);
+    }
+    return ctxs[0];
+  }
+
+  private onRenameClosing(ev: CustomEvent) {
+    this.renameTo = (this.shadowRoot!.querySelector("#renameField")! as TextField).value;
+  }
+
+  private onRenameClosed(ev: CustomEvent) {
+    this.renameDialogOpen = false;
+    if (ev.detail.action == "ok") {
+      var label = this.graph.Uri("rdfs:label");
+      if (this.uri === undefined) {
+        throw "lost uri";
+      }
+      const ctx = this.whatCtxHeldTheObj(this.uri, label);
+      this.graph.patchObject(this.uri, label, this.graph.Literal(this.renameTo), ctx);
+    }
+    this.renameTo = "";
+  }
+}