Extract QueryInput to its own component.
Add larger modal text editor. Include JSON representation in JS filtering.
This commit is contained in:
parent
bd0967df0e
commit
368da3360a
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
export let confirm = 'Confirm'
|
||||||
export let level = ''
|
export let level = ''
|
||||||
export let title
|
export let title
|
||||||
export let onCancel = () => {}
|
export let onCancel = () => {}
|
||||||
|
@ -15,8 +16,8 @@
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-button-row">
|
<div class="modal-button-row">
|
||||||
<button on:click={onCancel}>Cancel</button>
|
<button on:click={onCancel} style={onCancel ? '' : 'visibility: hidden;'}>Cancel</button>
|
||||||
<button class={level} on:click={onConfirm}>Confirm</button>
|
<button class={level} on:click={onConfirm}>{confirm}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +41,7 @@
|
||||||
/* Modal Content/Box */
|
/* Modal Content/Box */
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background-color: #fefefe;
|
background-color: #fefefe;
|
||||||
margin: 15% auto; /* 15% from the top and centered */
|
margin: /*15%*/ auto; /* 15% from the top and centered */
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
border: 1px solid #888;
|
border: 1px solid #888;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
<script>
|
||||||
|
export let big
|
||||||
|
export let jsFilter
|
||||||
|
export let mutableObjects
|
||||||
|
export let queryCode
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="query-input-header">
|
||||||
|
<div on:click={() => jsFilter = !jsFilter}>
|
||||||
|
Use JavaScript to filter messages
|
||||||
|
<input type=checkbox bind:checked={jsFilter}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if jsFilter}
|
||||||
|
<div class="query-input-display">
|
||||||
|
<div title="If enabled, mutations made by the below code will be displayed in the result data.">
|
||||||
|
Allow JavaScript to mutate objects
|
||||||
|
<input type=checkbox bind:checked={mutableObjects}>
|
||||||
|
</div>
|
||||||
|
<textarea class={"query-input" + (big ? ' big' : '')} bind:value={queryCode}></textarea>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.query-input-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.query-input-display {
|
||||||
|
margin-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.query-input {
|
||||||
|
min-height: 5vw;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.query-input.big {
|
||||||
|
height: 60vh;
|
||||||
|
width: 60vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
background-color: #222222;
|
||||||
|
color: #d0dde9;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
padding: 1em;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-width: 20vw;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -39,6 +39,7 @@ export const testMode = true
|
||||||
export const state = writable({
|
export const state = writable({
|
||||||
items: [],
|
items: [],
|
||||||
itemCount: undefined,
|
itemCount: undefined,
|
||||||
|
matchCount: undefined,
|
||||||
error: 'Connecting to WebSocket...',
|
error: 'Connecting to WebSocket...',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ const updateClearError = updater => state.update(s => {
|
||||||
let ws;
|
let ws;
|
||||||
let itemLimit = Infinity
|
let itemLimit = Infinity
|
||||||
let disconnected = false
|
let disconnected = false
|
||||||
let filterFunc = (_message, _value) => true
|
let filterFunc = (_message, _value, _json) => true
|
||||||
|
|
||||||
let runTestQuery = true
|
let runTestQuery = true
|
||||||
const testTimeout = 200
|
const testTimeout = 200
|
||||||
|
@ -60,9 +61,13 @@ const testQuery = (mode) => {
|
||||||
if (mode === queryMode.REAL_TIME) {
|
if (mode === queryMode.REAL_TIME) {
|
||||||
const addItem = () => {
|
const addItem = () => {
|
||||||
const item = getRandomFromArray(mockItems)
|
const item = getRandomFromArray(mockItems)
|
||||||
if (filterFunc(item, item.value)) {
|
if (filterFunc(item, item.value, JSON.stringify(item))) {
|
||||||
item.timestamp = new Date().getTime()
|
item.timestamp = new Date().getTime()
|
||||||
state.update(s => ({ ...s, items: [item, ...s.items].slice(0, itemLimit), itemCount: (s.itemCount || 0) + 1 }))
|
state.update(s => ({
|
||||||
|
...s,
|
||||||
|
items: [item, ...s.items].slice(0, itemLimit),
|
||||||
|
itemCount: (s.itemCount || 0) + 1
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
if (runTestQuery) {
|
if (runTestQuery) {
|
||||||
setTimeout(addItem, testTimeout)
|
setTimeout(addItem, testTimeout)
|
||||||
|
@ -70,7 +75,11 @@ const testQuery = (mode) => {
|
||||||
}
|
}
|
||||||
setTimeout(addItem, testTimeout)
|
setTimeout(addItem, testTimeout)
|
||||||
} else {
|
} else {
|
||||||
state.update(s => ({ ...s, items: mockItems.filter(item => filterFunc(item, item.value)), itemCount: (s.itemCount || 0) + 1 }))
|
state.update(s => ({
|
||||||
|
...s,
|
||||||
|
items: mockItems.filter(item => filterFunc(item, item.value, JSON.stringify(item))),
|
||||||
|
itemCount: (s.itemCount || 0) + 1
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Caught an error:', e.toString())
|
console.log('Caught an error:', e.toString())
|
||||||
|
@ -89,7 +98,7 @@ export const killQuery = async ({ }) => {
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
export const query = async ({ cluster, topic, mode, jsFilter, queryCode, maxItems, mutableObjects }) => {
|
export const query = async ({ cluster, topic, mode, jsFilter, queryCode, maxItems, mutableObjects }) => {
|
||||||
if (jsFilter) {
|
if (jsFilter) {
|
||||||
filterFunc = new Function('message', 'value', queryCode)
|
filterFunc = new Function('message', 'value', 'json', queryCode)
|
||||||
} else {
|
} else {
|
||||||
filterFunc = () => true
|
filterFunc = () => true
|
||||||
}
|
}
|
||||||
|
@ -153,7 +162,7 @@ export const connect = () => {
|
||||||
}))
|
}))
|
||||||
break;
|
break;
|
||||||
case 'message':
|
case 'message':
|
||||||
if (filterFunc(data.message)) {
|
if (filterFunc(data.message, data.message.value, message.data)) {
|
||||||
updateClearError(s => ({
|
updateClearError(s => ({
|
||||||
...s,
|
...s,
|
||||||
items: console.log('new item', data.message) || [data.message, ...s.items].slice(0, itemLimit),
|
items: console.log('new item', data.message) || [data.message, ...s.items].slice(0, itemLimit),
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { JsonView } from '@zerodevx/svelte-json-view'
|
import { JsonView } from '@zerodevx/svelte-json-view'
|
||||||
|
import QueryInput from '$lib/QueryInput.svelte'
|
||||||
import { state, connect, query, killQuery } from '$lib/state'
|
import { state, connect, query, killQuery } from '$lib/state'
|
||||||
import { queryMode } from "../lib/constants.js"
|
import { queryMode } from "../lib/constants.js"
|
||||||
import { apiFetch } from "../utils.js"
|
import { apiFetch } from "../utils.js"
|
||||||
|
import Modal from "../lib/Modal.svelte";
|
||||||
|
|
||||||
|
let showQueryModal = false
|
||||||
|
|
||||||
let expandAll = true
|
let expandAll = true
|
||||||
let showMetadata = true
|
let showMetadata = true
|
||||||
|
@ -16,8 +20,10 @@
|
||||||
maxItems: 20,
|
maxItems: 20,
|
||||||
mutableObjects: false,
|
mutableObjects: false,
|
||||||
queryCode:
|
queryCode:
|
||||||
`// The message object contains all metadata
|
`// \`message\` contains all metadata
|
||||||
// \`value\` is shorthand for message.value
|
// \`value\` is shorthand for message.value
|
||||||
|
// it contains the actual produced data
|
||||||
|
// \`json\` is a JSON representation of \`message\`
|
||||||
|
|
||||||
return message.value.eventType === "Television";`
|
return message.value.eventType === "Television";`
|
||||||
}
|
}
|
||||||
|
@ -123,14 +129,24 @@ return message.value.eventType === "Television";`
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div class="settings-option">
|
<div class="settings-option">
|
||||||
<div class="query-input-header"><div on:click={() => querySettings.jsFilter = !querySettings.jsFilter}>Use JavaScript to filter messages</div> <input type=checkbox bind:checked={querySettings.jsFilter}></div>
|
<QueryInput
|
||||||
|
bind:jsFilter={querySettings.jsFilter}
|
||||||
|
bind:mutableObjects={querySettings.mutableObjects}
|
||||||
|
bind:queryCode={querySettings.queryCode} />
|
||||||
{#if querySettings.jsFilter}
|
{#if querySettings.jsFilter}
|
||||||
<div class="query-input-display">
|
<button class="query-input-button" on:click={() => showQueryModal = true}>Open Large Editor</button>
|
||||||
<div title="If enabled, mutations made by the below code will be displayed in the result data.">
|
{/if}
|
||||||
Allow JavaScript to mutate objects <input type=checkbox bind:checked={querySettings.mutableObjects}>
|
{#if showQueryModal}
|
||||||
</div>
|
<Modal onClickOut={() => showQueryModal = false}
|
||||||
<textarea class="query-input" bind:value={querySettings.queryCode}></textarea>
|
onCancel={null}
|
||||||
</div>
|
onConfirm={() => showQueryModal = false}
|
||||||
|
confirm="Close"
|
||||||
|
title={'JS Filter'}>
|
||||||
|
<QueryInput big
|
||||||
|
bind:jsFilter={querySettings.jsFilter}
|
||||||
|
bind:mutableObjects={querySettings.mutableObjects}
|
||||||
|
bind:queryCode={querySettings.queryCode} />
|
||||||
|
</Modal>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -167,21 +183,25 @@ return message.value.eventType === "Television";`
|
||||||
|
|
||||||
<div class="data-view-container">
|
<div class="data-view-container">
|
||||||
<div class="data-view-bar">
|
<div class="data-view-bar">
|
||||||
<div>
|
<div class="border-between">
|
||||||
<button class={expandAll ? 'selected' : ''} on:click={() => expandAll = !expandAll}>
|
<button class={expandAll ? 'selected' : ''} on:click={() => expandAll = !expandAll}>
|
||||||
Expand All Objects
|
Expand All Objects
|
||||||
</button>
|
</button>
|
||||||
<button class={showMetadata ? 'selected' : ''} on:click={() => showMetadata = !showMetadata}>
|
<button class={showMetadata ? 'selected' : ''} on:click={() => showMetadata = !showMetadata}>
|
||||||
Show Metadata
|
Show Metadata
|
||||||
</button>
|
</button>
|
||||||
<button on:click={fontUp}>+</button>
|
|
||||||
<button on:click={fontDown}>-</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
{#if $state.itemCount >= 0}
|
{#if $state.itemCount >= 0}
|
||||||
<div class="data-view-results">
|
<div class="data-view-results">
|
||||||
{$state.itemCount} Messages
|
{$state.itemCount} Messages
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="border-between">
|
||||||
|
<button on:click={fontDown} style="border-color: #aaa;">-</button>
|
||||||
|
<button on:click={fontUp}>+</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if $state.items?.length > 0}
|
{#if $state.items?.length > 0}
|
||||||
<div class="data-view" style={`font-size: ${fontSizes[dataViewFontSize]}%`}>
|
<div class="data-view" style={`font-size: ${fontSizes[dataViewFontSize]}%`}>
|
||||||
|
@ -212,10 +232,21 @@ return message.value.eventType === "Television";`
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.data-view-bar button {
|
.border-between {
|
||||||
|
border-width: 1px;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.border-between button:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.data-view-bar button {
|
||||||
|
border-color: white;
|
||||||
|
border-style: none;
|
||||||
|
border-right: solid;
|
||||||
|
border-width: 1px;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
margin-right: 1px;
|
margin: 0;
|
||||||
}
|
}
|
||||||
button.selected {
|
button.selected {
|
||||||
background-color: #4732a5;
|
background-color: #4732a5;
|
||||||
|
@ -243,20 +274,14 @@ return message.value.eventType === "Television";`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
.query-input-button {
|
||||||
overflow: hidden;
|
border-style: none;
|
||||||
z-index: 1;
|
border-radius: 0;
|
||||||
margin-left: 0;
|
background-color: #313131;
|
||||||
|
|
||||||
background-color: #222222;
|
|
||||||
color: #d0dde9;
|
color: #d0dde9;
|
||||||
|
}
|
||||||
border: none;
|
.query-input-button:hover {
|
||||||
font-size: 14px;
|
background-color: #575757;
|
||||||
font-family: var(--font-mono);
|
|
||||||
padding: 1em;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 20vw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-settings {
|
.query-settings {
|
||||||
|
@ -269,21 +294,6 @@ return message.value.eventType === "Television";`
|
||||||
height: 83vh;
|
height: 83vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-input-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.query-input-display {
|
|
||||||
margin-top: 1em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.query-input {
|
|
||||||
min-height: 4vw;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-type {
|
.query-type {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
Loading…
Reference in New Issue