1<script> 2 import { onMount, tick } from "svelte"; 3 4 import { get } from "../api"; 5 import { query_shell_history, addToHistory } from "../stores/query"; 6 import { getFilterParams } from "../stores/filters"; 7 import { parseQueryChart } from "../charts"; 8 9 import Chart from "../charts/Chart.svelte"; 10 import QueryEditor from "./QueryEditor.svelte"; 11 import QueryLinks from "./QueryLinks.svelte"; 12 13 let query_string = ""; 14 15 /** @type {Record<string,HTMLElement>} */ 16 const resultElems = {}; 17 18 /** @typedef {{result?: { table: string, chart: ReturnType<parseQueryChart> }, error?: unknown}} ResultType 19 /** @type {Record<string,ResultType>} */ 20 const query_results = {}; 21 22 $: query_result_array = $query_shell_history.map( 23 /** @returns {[string, ResultType]} */ (item) => [ 24 item, 25 query_results[item] || {}, 26 ] 27 ); 28 29 /** 30 * @param {string} query 31 * @param {ResultType} res 32 */ 33 async function setResult(query, res) { 34 addToHistory(query); 35 query_results[query] = res; 36 await tick(); 37 const url = new URL(window.location.href); 38 url.searchParams.set("query_string", query); 39 window.history.replaceState(null, "", url.toString()); 40 resultElems[query].setAttribute("open", "true"); 41 } 42 43 function submit() { 44 const query = query_string; 45 if (!query) { 46 return; 47 } 48 get("query_result", { query_string: query, ...getFilterParams() }).then( 49 (res) => { 50 const chart = parseQueryChart(res.chart); 51 setResult(query, { result: { chart, table: res.table } }); 52 }, 53 (error) => { 54 setResult(query, { error }); 55 } 56 ); 57 } 58 59 /** 60 * @param {string} query 61 */ 62 function click(query) { 63 if (!query_results[query]) { 64 query_string = query; 65 submit(); 66 } 67 } 68 69 onMount(() => { 70 const url = new URL(window.location.href); 71 query_string = url.searchParams.get("query_string") || ""; 72 if (query_string) { 73 submit(); 74 } 75 }); 76</script> 77 78<QueryEditor bind:value={query_string} on:submit={submit} /> 79<div> 80 {#each query_result_array as [history_item, { result, error }] (history_item)} 81 <details class:error bind:this={resultElems[history_item]}> 82 <summary on:click={() => click(history_item)}> 83 <pre> 84 <code>{history_item}</code> 85 </pre> 86 {#if result} 87 <span class="spacer" /> 88 <QueryLinks query={history_item} /> 89 {/if} 90 </summary> 91 <div> 92 {#if result} 93 {#if result.chart} 94 <Chart chart={result.chart} /> 95 {/if} 96 {@html result.table} 97 {:else if error} 98 {@html error} 99 {/if} 100 </div> 101 </details> 102 {/each} 103</div> 104 105<style> 106 details > div { 107 max-height: 80vh; 108 overflow: auto; 109 } 110 111 div :global(.query-error) { 112 font-family: var(--font-family-monospaced); 113 color: var(--color-background); 114 background: var(--color-error); 115 } 116</style> 117