mirror of
https://github.com/garraflavatra/rolens.git
synced 2025-01-18 21:17:59 +00:00
Use Webdesq objecttree instead highlight.js
This commit is contained in:
parent
44d4fb843d
commit
a4698eee43
16
frontend/package-lock.json
generated
16
frontend/package-lock.json
generated
@ -7,9 +7,6 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
|
||||||
"highlight.js": "^11.7.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||||
"eslint": "^8.31.0",
|
"eslint": "^8.31.0",
|
||||||
@ -1891,14 +1888,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/highlight.js": {
|
|
||||||
"version": "11.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz",
|
|
||||||
"integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.2.4",
|
"version": "5.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||||
@ -4534,11 +4523,6 @@
|
|||||||
"has-symbols": "^1.0.2"
|
"has-symbols": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"highlight.js": {
|
|
||||||
"version": "11.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz",
|
|
||||||
"integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ=="
|
|
||||||
},
|
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "5.2.4",
|
"version": "5.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||||
|
@ -21,8 +21,5 @@
|
|||||||
"nodejs",
|
"nodejs",
|
||||||
"svelte3"
|
"svelte3"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"highlight.js": "^11.7.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
d445254075c563425c5b0fd21e804d4a
|
e25516ba8abdacf5ab40609a5d48f542
|
@ -1,18 +1,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import CodeViewer from './codeviewer.svelte';
|
|
||||||
|
|
||||||
export let code = '';
|
export let code = '';
|
||||||
|
|
||||||
let modalCode = '';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="examplecode" on:pointerdown={() => modalCode = code}>
|
<div class="examplecode">
|
||||||
<strong>CLI command</strong>
|
<strong>CLI command</strong>
|
||||||
<code>{code}</code>
|
<code>{code}</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CodeViewer bind:code={modalCode} language="js" />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.examplecode {
|
.examplecode {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
<script>
|
|
||||||
import hljs from 'highlight.js/lib/core';
|
|
||||||
import hljsJSON from 'highlight.js/lib/languages/json';
|
|
||||||
import hljsJavaScript from 'highlight.js/lib/languages/javascript';
|
|
||||||
import Icon from './icon.svelte';
|
|
||||||
import Modal from './modal.svelte';
|
|
||||||
import 'highlight.js/styles/atom-one-dark.css';
|
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
|
|
||||||
export let code = '';
|
|
||||||
export let language = 'json';
|
|
||||||
|
|
||||||
const languageNames = {
|
|
||||||
json: 'JSON',
|
|
||||||
js: 'JavaScript',
|
|
||||||
};
|
|
||||||
|
|
||||||
hljs.registerLanguage('json', hljsJSON);
|
|
||||||
hljs.registerLanguage('js', hljsJavaScript);
|
|
||||||
|
|
||||||
let copySucceeded = false;
|
|
||||||
let timeout;
|
|
||||||
$: highlighted = code ? hljs.highlight(code, { language }).value : '';
|
|
||||||
|
|
||||||
async function copy() {
|
|
||||||
await navigator.clipboard.writeText(code);
|
|
||||||
copySucceeded = true;
|
|
||||||
timeout = setTimeout(() => copySucceeded = false, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDestroy(() => clearTimeout(timeout));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if code}
|
|
||||||
<Modal bind:show={code} title="{languageNames[language]} viewer" contentPadding={false}>
|
|
||||||
<div class="codeblock">
|
|
||||||
<div class="buttons">
|
|
||||||
<button class="btn" on:click={copy}>
|
|
||||||
<Icon name={copySucceeded ? 'check' : 'clipboard'} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<pre><code class="hljs">{@html highlighted}</code></pre>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.codeblock {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.buttons {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
.buttons button {
|
|
||||||
margin-left: 1rem;
|
|
||||||
background: none;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
.buttons button:hover {
|
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
user-select: text;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre :global(::selection) {
|
|
||||||
background: rgba(5, 5, 5, 0.8);
|
|
||||||
}
|
|
||||||
</style>
|
|
208
frontend/src/components/objecttree.svelte
Normal file
208
frontend/src/components/objecttree.svelte
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
<script>
|
||||||
|
export let data;
|
||||||
|
export let depth = 0;
|
||||||
|
export let readonly = false;
|
||||||
|
export let level = 0;
|
||||||
|
export let last = true;
|
||||||
|
export let draggable = false;
|
||||||
|
export let kp = '';
|
||||||
|
|
||||||
|
const collapsedSymbol = '...';
|
||||||
|
const getType = i => {
|
||||||
|
if (i === null) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
return typeof i;
|
||||||
|
};
|
||||||
|
|
||||||
|
let displayOnly = true;
|
||||||
|
let items;
|
||||||
|
let isArray;
|
||||||
|
let openBracket;
|
||||||
|
let closeBracket;
|
||||||
|
$: {
|
||||||
|
items = getType(data) === 'object' ? Object.keys(data) : [];
|
||||||
|
isArray = Array.isArray(data);
|
||||||
|
openBracket = isArray ? '[' : '{';
|
||||||
|
closeBracket = isArray ? ']' : '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
let collapsed;
|
||||||
|
$: collapsed = depth < level;
|
||||||
|
|
||||||
|
const format = i => {
|
||||||
|
switch (getType(i)) {
|
||||||
|
case 'string':
|
||||||
|
return `${i}`;
|
||||||
|
case 'function':
|
||||||
|
return 'f () {...}';
|
||||||
|
case 'symbol':
|
||||||
|
return i.toString();
|
||||||
|
default:
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const clicked = e => {
|
||||||
|
if (e.shiftKey) {
|
||||||
|
if (depth == 0) {
|
||||||
|
depth = 999;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
depth = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collapsed = !collapsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
let invalid = false;
|
||||||
|
let dbg;
|
||||||
|
|
||||||
|
function json2data() {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(dbg.value);
|
||||||
|
invalid = false;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
invalid = true;
|
||||||
|
if (dbg.value.trim == '') {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragstart(e, keypath, value) {
|
||||||
|
console.log('kp:', keypath);
|
||||||
|
const item = {};
|
||||||
|
item[keypath] = value;
|
||||||
|
e.dataTransfer.setData('text/plain', JSON.stringify(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeydown(event) {
|
||||||
|
const save = (event.key === 's') && (event.metaKey || event.ctrlKey);
|
||||||
|
if (!save) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if displayOnly}
|
||||||
|
{#if items.length}
|
||||||
|
<span class:root={level == 0} class:hidden={collapsed}>
|
||||||
|
{#if draggable && isArray}
|
||||||
|
<span on:dragstart={e => dragstart(e, kp, data)} draggable="true" class="bracket" on:click={clicked} tabindex="0">{openBracket}</span>
|
||||||
|
{:else}
|
||||||
|
<span class="bracket" on:click={clicked} tabindex="0">{openBracket}</span>
|
||||||
|
{/if}
|
||||||
|
<ul on:dblclick={() => (readonly ? displayOnly = true : displayOnly = false)} >
|
||||||
|
{#each items as i, idx}
|
||||||
|
<li>
|
||||||
|
{#if !isArray}
|
||||||
|
{#if draggable}
|
||||||
|
<span on:dragstart={e => dragstart(e, kp ? kp + '.' + i : i, data[i])} draggable="true" class="key">{i}:</span>
|
||||||
|
{:else}
|
||||||
|
<span class="key">{i}:</span>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{#if getType(data[i]) === 'object'}
|
||||||
|
<svelte:self {readonly} {draggable} kp={kp ? kp + '.' + i : i} data={data[i]} {depth} level={level + 1} last={idx === items.length - 1} />
|
||||||
|
{:else}
|
||||||
|
{#if draggable}
|
||||||
|
<span on:dragstart={e => dragstart(e, kp ? kp + '.' + i : i, data[i])} draggable="true" class="val {getType(data[i])}">{format(data[i])}</span>{#if idx < items.length - 1}<span draggable class="comma">,</span>{/if}
|
||||||
|
{:else}
|
||||||
|
<span class="val {getType(data[i])}">{format(data[i])}</span>{#if idx < items.length - 1}<span class="comma">,</span>{/if}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<span class="bracket" on:click={clicked} tabindex="0">{closeBracket}</span>{#if !last}<span
|
||||||
|
class="comma">,</span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<span style="padding: {level == 0 ? 10 : 0}px;" class="bracket" class:hidden={!collapsed} on:click={clicked} tabindex="0">{openBracket}{collapsedSymbol}{closeBracket}</span>{#if !last && collapsed}<span class="comma">,</span>{/if}
|
||||||
|
{:else}
|
||||||
|
{@html isArray ? '[]' : '{}'}
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<textarea on:keydown="{onKeydown}" class="debug" spellcheck="false" bind:this={dbg} class:invalid on:input={json2data}>{JSON.stringify(data, null, 2)}</textarea>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding-left: var(--nodePaddingLeft, 1rem);
|
||||||
|
border-left: var(--nodeBorderLeft, 1px dashed #d0d0f0);
|
||||||
|
color: var(--nodeColor, #666);
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.root {
|
||||||
|
font-family: menlo, monospace;
|
||||||
|
font-size: 90%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.bracket {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.bracket:hover {
|
||||||
|
background: var(--bracketHoverBackground, #d1d5db);
|
||||||
|
}
|
||||||
|
.comma {
|
||||||
|
color: var(--nodeColor, #374151);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
color: var(--leafDefaultColor, #9ca3af);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.val[draggable] {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
.val.string {
|
||||||
|
color: var(--leafStringColor, #000);
|
||||||
|
}
|
||||||
|
.val.string:before {
|
||||||
|
content: "'";
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.val.string:after {
|
||||||
|
content: "'";
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.val.number {
|
||||||
|
color: var(--leafNumberColor, #d97706);
|
||||||
|
}
|
||||||
|
.val.boolean {
|
||||||
|
color: var(--leafBooleanColor, #3994dd);
|
||||||
|
}
|
||||||
|
.key.draggable {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.debug {
|
||||||
|
font-family: menlo, monospace;
|
||||||
|
padding: 10px;
|
||||||
|
flex: 1 0;
|
||||||
|
white-space: pre;
|
||||||
|
font-size: 90%;
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.invalid {
|
||||||
|
background: #ffe3e3;
|
||||||
|
color: #b30202 !important;
|
||||||
|
}
|
||||||
|
</style>
|
52
frontend/src/components/objectviewer.svelte
Normal file
52
frontend/src/components/objectviewer.svelte
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script>
|
||||||
|
import Icon from './icon.svelte';
|
||||||
|
import Modal from './modal.svelte';
|
||||||
|
import ObjectTree from './objecttree.svelte';
|
||||||
|
import { onDestroy } from 'svelte';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
let copySucceeded = false;
|
||||||
|
let timeout;
|
||||||
|
|
||||||
|
async function copy() {
|
||||||
|
await navigator.clipboard.writeText(JSON.stringify(data));
|
||||||
|
copySucceeded = true;
|
||||||
|
timeout = setTimeout(() => copySucceeded = false, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => clearTimeout(timeout));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if data}
|
||||||
|
<Modal bind:show={data} title="Object viewer" contentPadding={false}>
|
||||||
|
<div class="objectviewer">
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="btn" on:click={copy}>
|
||||||
|
<Icon name={copySucceeded ? 'check' : 'clipboard'} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="code">
|
||||||
|
<ObjectTree {data} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.objectviewer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.code {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
.buttons button {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,7 +4,7 @@
|
|||||||
import { input } from '../../../actions';
|
import { input } from '../../../actions';
|
||||||
import ObjectGrid from '../../../components/objectgrid.svelte';
|
import ObjectGrid from '../../../components/objectgrid.svelte';
|
||||||
import Icon from '../../../components/icon.svelte';
|
import Icon from '../../../components/icon.svelte';
|
||||||
import CodeViewer from '../../../components/codeviewer.svelte';
|
import ObjectViewer from '../../../components/objectviewer.svelte';
|
||||||
|
|
||||||
export let collection;
|
export let collection;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@
|
|||||||
let submittedForm = {};
|
let submittedForm = {};
|
||||||
let queryField;
|
let queryField;
|
||||||
let activePath = '';
|
let activePath = '';
|
||||||
let json = '';
|
let objectViewerData;
|
||||||
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
|
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
|
||||||
|
|
||||||
async function submitQuery() {
|
async function submitQuery() {
|
||||||
@ -57,10 +57,7 @@
|
|||||||
|
|
||||||
function openJson(itemId) {
|
function openJson(itemId) {
|
||||||
const item = result?.results?.find(i => i._id == itemId);
|
const item = result?.results?.find(i => i._id == itemId);
|
||||||
if (!item) {
|
objectViewerData = item;
|
||||||
return;
|
|
||||||
}
|
|
||||||
json = JSON.stringify(item, undefined, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function performQuery(q) {
|
export function performQuery(q) {
|
||||||
@ -133,7 +130,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CodeViewer bind:code={json} language="json" />
|
<ObjectViewer bind:data={objectViewerData} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.find {
|
.find {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import CodeViewer from '../../../components/codeviewer.svelte';
|
import ObjectViewer from '../../../components/objectviewer.svelte';
|
||||||
import ObjectGrid from '../../../components/objectgrid.svelte';
|
import ObjectGrid from '../../../components/objectgrid.svelte';
|
||||||
import { DropIndex, GetIndexes } from '../../../../wailsjs/go/app/App';
|
import { DropIndex, GetIndexes } from '../../../../wailsjs/go/app/App';
|
||||||
|
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
let indexes = [];
|
let indexes = [];
|
||||||
let activeKey = '';
|
let activeKey = '';
|
||||||
let json = '';
|
let objectViewerData = '';
|
||||||
|
|
||||||
async function getIndexes() {
|
async function getIndexes() {
|
||||||
const result = await GetIndexes(collection.hostKey, collection.dbKey, collection.key);
|
const result = await GetIndexes(collection.hostKey, collection.dbKey, collection.key);
|
||||||
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
function openJson(indexId) {
|
function openJson(indexId) {
|
||||||
const item = indexes?.filter(i => i.name == indexId);
|
const item = indexes?.filter(i => i.name == indexId);
|
||||||
json = JSON.stringify(item, undefined, 2);
|
objectViewerData = item;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CodeViewer bind:code={json} language="json" />
|
<ObjectViewer bind:data={objectViewerData} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.indexes {
|
.indexes {
|
||||||
|
Loading…
Reference in New Issue
Block a user