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"> <textarea id="search" rows=8 style="flex: 1" @keydown=${this.search_keydown}>${this.query}</textarea> <input type="button" value="Execute" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}></input> </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);