import {LitElement, html, unsafeHTML} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; import {styles} from './tf-styles.js'; class TfTabQueryElement extends LitElement { static get properties() { return { whoami: {type: String}, users: {type: Object}, following: {type: Array}, query: {type: String}, expanded: {type: Object}, results: {type: Array}, error: {type: Object}, duration: {type: Number}, }; } static styles = styles; constructor() { super(); let self = this; this.whoami = null; this.users = {}; this.following = []; this.expanded = {}; this.duration = undefined; } async search(query) { console.log('Searching...', this.whoami, query); this.results = []; this.error = undefined; this.duration = undefined; let search = this.renderRoot.getElementById('search'); if (search) { search.value = query; search.focus(); } await tfrpc.rpc.setHash('#sql=' + encodeURIComponent(query)); let start_time = new Date(); try { this.results = await tfrpc.rpc.query(query, []); } catch (error) { this.error = error; } let end_time = new Date(); this.duration = (end_time - start_time).valueOf(); console.log('Done.'); search = this.renderRoot.getElementById('search'); if (search) { search.value = query; search.focus(); } } search_keydown(event) { if (event.keyCode == 13 && event.ctrlKey) { this.query = this.renderRoot.getElementById('search').value; event.preventDefault(); } } on_expand(event) { if (event.detail.expanded) { let expand = {}; expand[event.detail.id] = true; this.expanded = Object.assign({}, this.expanded, expand); } else { delete this.expanded[event.detail.id]; this.expanded = Object.assign({}, this.expanded); } } render_results() { if (!this.results?.length) { return html`<div>No results.</div>`; } else { let keys = Object.keys(this.results[0]).sort(); return html`<table style="width: 100%; max-width: 100%"> <tr> ${keys.map((key) => html`<th>${key}</th>`)} </tr> ${this.results.map( (row) => html`<tr> ${keys.map((key) => html`<td>${row[key]}</td>`)} </tr>` )} </table>`; } } render_error() { if (this.error) { return html`<h2 style="color: red">${this.error.message}</h2> <pre style="color: red">${this.error.stack}</pre>`; } } render() { if (this.query !== this.last_query) { this.last_query = this.query; this.search(this.query); } let self = this; return html` <div style="display: flex; flex-direction: row; gap: 4px"> <textarea id="search" rows="8" class="w3-input w3-theme-d1" style="flex: 1; resize: vertical" @keydown=${this.search_keydown} > ${this.query}</textarea > <button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)} > Execute </button> </div> <div ?hidden=${this.duration === undefined}> Took ${this.duration / 1000.0} seconds. </div> <div ?hidden=${this.duration !== undefined}>Executing...</div> ${this.render_error()} ${this.render_results()} `; } } customElements.define('tf-tab-query', TfTabQueryElement);