1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-07-19 14:14:05 +00:00
This commit is contained in:
2023-01-29 20:00:15 +01:00
parent 2d33c6f2ab
commit 1cf89b3278
20 changed files with 817 additions and 370 deletions

View File

@ -0,0 +1,97 @@
<script>
import { resolveKeypath, setValue } from '../../../../utils';
import Icon from '../../../../components/icon.svelte';
import FormInput from './forminput.svelte';
export let item = {};
export let view = {};
export let valid = false;
const validity = {};
$: valid = Object.values(validity).every(v => !!v);
const iconMap = {
string: 'text',
int: 'hash',
long: 'hash',
uint64: 'hash',
double: 'hash',
decimal: 'hash',
bool: 'toggle-l',
date: 'cal',
};
const keypathProxy = new Proxy(item, {
get: (item, key) => resolveKeypath(item, key),
set: (item, key, value) => {
setValue(item, key, value);
return true;
},
});
function reset(columnKey) {
keypathProxy[columnKey] = undefined;
}
</script>
{#if item && view}
{#each view?.columns?.filter(c => c.inputType !== 'none') || [] as column}
<!-- svelte-ignore a11y-label-has-associated-control because FormInput contains one -->
<label class="column">
<div class="label">
<Icon name={iconMap[column.inputType]} />
<span>
{column.key}
{#if column.mandatory}
<span class="tag" class:invalid={!validity[column.key]}>mandatory</span>
{/if}
</span>
</div>
<div class="input">
<FormInput {column} bind:value={keypathProxy[column.key]} bind:valid={validity[column.key]} />
<button type="button" class="btn" title="Reset value" on:click={() => reset(column.key)} disabled={!keypathProxy[column.key]}>
<Icon name="reload" />
</button>
</div>
</label>
{/each}
{/if}
<style>
.column {
display: block;
}
.column + .column {
margin-top: 1rem;
}
.column .label {
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.column .label :global(svg) {
width: 13px;
height: 13px;
}
.input {
display: grid;
grid-template: 1fr / 1fr auto;
gap: 0.5rem;
}
.tag {
display: inline-block;
background-color: rgba(140, 140, 140, 0.1);
color: #777;
text-transform: uppercase;
font-size: 10px;
padding: 3px 5px;
font-weight: 600;
}
.tag.invalid {
background-color: rgba(255, 80, 80, 0.3);
color: #8d2c2c;
}
</style>

View File

@ -0,0 +1,76 @@
<script>
import { isDate } from '../../../../utils';
import { createEventDispatcher } from 'svelte';
import { input } from '../../../../actions';
export let column = {};
export let value = undefined;
export let valid = true;
const dispatch = createEventDispatcher();
const onValid = () => valid = true;
const onInvalid = () => valid = false;
const numericTypes = [ 'int', 'long', 'uint64', 'double', 'decimal' ];
let dateInput;
let timeInput;
$: type = column.inputType;
$: mandatory = column.mandatory;
$: dispatch('input', value);
$: if (value === undefined) {
dateInput && (dateInput.value = undefined);
timeInput && (timeInput.value = undefined);
mandatory && (valid = false);
}
function setDate(event) {
if (event?.currentTarget?.value) {
if (!isDate(value)) {
value = new Date(event.currentTarget.value);
}
const date = event.currentTarget.value.split('-').map(n => parseInt(n));
value.setFullYear(date[0], date[1], date[2]);
}
}
function setTime(event) {
if (event?.currentTarget?.value) {
const time = event.currentTarget.value.split(':').map(n => parseInt(n));
value.setHours?.(time[0], time[1], 0, 0);
}
}
function selectChange() {
if ((value === undefined) && mandatory) {
valid = false;
}
else {
valid = true;
}
}
</script>
<div class="field {type}">
{#if type === 'string'}
<input type="text" bind:value use:input={{ type, onValid, onInvalid, mandatory }} />
{:else if numericTypes.includes(type)}
<input type="number" bind:value use:input={{ type, onValid, onInvalid, mandatory }} />
{:else if type === 'bool'}
<select bind:value on:change={selectChange}>
<option value={undefined} disabled={mandatory}>Unset</option>
<option value={true}>Yes / true</option>
<option value={false}>No / false</option>
</select>
{:else if type === 'date'}
{@const isNotDate = !isDate(value)}
<input type="date" bind:this={dateInput} on:input={setDate} use:input />
<input type="time" bind:this={timeInput} on:input={setTime} disabled={isNotDate} title={isNotDate ? 'Enter a date first' : ''} />
{/if}
</div>
<style>
.field.date {
display: grid;
grid-template: 1fr / 3fr 1fr;
}
</style>

View File

@ -1,8 +1,8 @@
<script>
import Icon from '../../../components/icon.svelte';
import { input } from '../../../actions';
import Modal from '../../../components/modal.svelte';
import { CreateIndex } from '../../../../wailsjs/go/app/App';
import Icon from '../../../../components/icon.svelte';
import { input } from '../../../../actions';
import Modal from '../../../../components/modal.svelte';
import { CreateIndex } from '../../../../../wailsjs/go/app/App';
import { createEventDispatcher } from 'svelte';
export let collection = {};

View File

@ -0,0 +1,242 @@
<script>
import TabBar from '../../../../components/tabbar.svelte';
import Modal from '../../../../components/modal.svelte';
import Icon from '../../../../components/icon.svelte';
import { views } from '../../../../stores';
import { randomString } from '../../../../utils';
import { input } from '../../../../actions';
export let collection;
export let show = false;
export let activeViewKey = 'list';
export let firstItem = {};
$: tabs = Object.entries(views.forCollection(collection.hostKey, collection.dbKey, collection.key))
.sort((a, b) => sortTabKeys(a[0], b[0]))
.map(([ key, v ]) => ({ key, title: v.name, closable: key !== 'list' }));
function sortTabKeys(a, b) {
if (a === 'list') {
return -1;
}
if (b === 'list') {
return 1;
}
else {
return a.localeCompare(b);
}
}
function createView() {
const newViewKey = randomString();
$views[newViewKey] = {
name: 'Table view',
host: collection.hostKey,
database: collection.dbKey,
collection: collection.key,
type: 'table',
columns: [ { key: '_id', showInTable: true, inputType: 'string' } ],
};
activeViewKey = newViewKey;
}
function removeView(viewKey) {
const keys = Object.keys($views).sort(sortTabKeys);
const oldIndex = keys.indexOf(viewKey);
const newKey = keys[oldIndex - 1];
activeViewKey = newKey;
delete $views[viewKey];
$views = $views;
}
function addColumn(before) {
if (typeof before === 'number') {
$views[activeViewKey].columns = [
...$views[activeViewKey].columns.slice(0, before),
{ showInTable: true, inputType: 'none' },
...$views[activeViewKey].columns.slice(before),
];
}
else {
$views[activeViewKey].columns = [
...$views[activeViewKey].columns,
{ showInTable: true, inputType: 'none' },
];
}
}
function addSuggestedColumns() {
if ((typeof firstItem !== 'object') || (firstItem === null)) {
return;
}
$views[activeViewKey].columns = Object.keys(firstItem).sort().map(key => ({
key,
showInTable: true,
inputType: 'none',
}));
}
function moveColumn(oldIndex, delta) {
const column = $views[activeViewKey].columns[oldIndex];
const newIndex = oldIndex + delta;
$views[activeViewKey].columns.splice(oldIndex, 1);
$views[activeViewKey].columns.splice(newIndex, 0, column);
$views[activeViewKey].columns = $views[activeViewKey].columns;
}
function removeColumn(index) {
$views[activeViewKey].columns.splice(index, 1);
$views[activeViewKey].columns = $views[activeViewKey].columns;
}
</script>
<Modal title="View configuration" bind:show contentPadding={false}>
<TabBar
{tabs}
canAddTab={true}
on:addTab={createView}
on:closeTab={e => removeView(e.detail)}
bind:selectedKey={activeViewKey}
/>
<div class="options">
{#if $views[activeViewKey]}
<div class="meta">
{#key activeViewKey}
<label class="field">
<span class="label">View name</span>
<input type="text" use:input={{ autofocus: true }} bind:value={$views[activeViewKey].name} disabled={activeViewKey === 'list'} />
</label>
{/key}
<label class="field">
<span class="label">View type</span>
<select bind:value={$views[activeViewKey].type} disabled>
<option value="list">List view</option>
<option value="table">Table view</option>
</select>
</label>
</div>
{#if $views[activeViewKey].type === 'list'}
<div class="flex">
<input type="checkbox" id="hideObjectIndicators" bind:checked={$views[activeViewKey].hideObjectIndicators} />
<label for="hideObjectIndicators">
Hide object indicators ({'{...}'} and [...]) in list view and show nothing instead
</label>
</div>
{:else if $views[activeViewKey].type === 'table'}
<div class="columns">
{#each $views[activeViewKey].columns as column, columnIndex}
<div class="column">
<label class="field">
<input type="text" use:input bind:value={column.key} placeholder="Column keypath" />
</label>
<label class="field" title="Show column in table view">
<span class="label">
<Icon name="table" />
</span>
<span class="checkbox">
<input type="checkbox" bind:checked={column.showInTable} />
</span>
</label>
<label class="field" title="Input type in form view">
<span class="label">
<Icon name="form" />
</span>
<select bind:value={column.inputType}>
<option value="none">Hidden in form</option>
<optgroup label="Strings">
<option value="string">String</option>
<option value="objectid">ObjectID</option>
</optgroup>
<optgroup label="Integers">
<option value="int">Integer (32-bit, signed)</option>
<option value="uint64">Integer (64-bit, unsigned)</option>
<option value="long">Long (64-bit integer, signed)</option>
</optgroup>
<optgroup label="Floats">
<option value="double">Double (64-bit)</option>
<option value="decimal">Decimal (128-bit)</option>
</optgroup>
<optgroup label="Miscellaneous">
<option value="bool">Boolean</option>
<option value="date">Date</option>
</optgroup>
</select>
</label>
<label class="field" title="Mandatory (field must be valid in order to submit form)">
<span class="label">
<Icon name="target" />
</span>
<span class="checkbox">
<input type="checkbox" bind:checked={column.mandatory} disabled={column.inputType === 'none'} />
</span>
</label>
<button class="btn" type="button" on:click={() => addColumn(columnIndex)} title="Add column before this one">
<Icon name="+" />
</button>
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, -1)} disabled={columnIndex === 0} title="Move column one position up">
<Icon name="chev-u" />
</button>
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, 1)} disabled={columnIndex === $views[activeViewKey].columns.length - 1} title="Move column one position down">
<Icon name="chev-d" />
</button>
<button class="btn danger" type="button" on:click={() => removeColumn(columnIndex)} title="Remove this column">
<Icon name="x" />
</button>
</div>
{:else}
<p>No columns yet</p>
{/each}
</div>
<button class="btn" on:click={addColumn}>
<Icon name="+" /> Add column
</button>
<button class="btn" on:click={addSuggestedColumns} disabled={!Object.keys(firstItem || {}).length}>
<Icon name="zap" /> Add suggested columns
</button>
{/if}
{/if}
</div>
</Modal>
<style>
.options {
padding: 1rem;
}
.meta {
display: grid;
grid-template: 1fr / 1fr 1fr;
gap: 0.5rem;
margin-bottom: 1rem;
}
.flex {
display: flex;
gap: 0.5rem;
align-items: center;
}
.columns {
border: 1px solid #ccc;
overflow: auto;
padding: 0.5rem 0.5rem 0;
margin-bottom: 0.5rem;
}
.columns p {
margin-bottom: 0.5rem;
}
.columns .column {
display: grid;
grid-template: 1fr / 1fr repeat(7, auto);
gap: 0.5rem;
margin-bottom: 0.5rem;
}
</style>

View File

@ -1,182 +0,0 @@
<script>
import TabBar from '../../../components/tabbar.svelte';
import Modal from '../../../components/modal.svelte';
import Icon from '../../../components/icon.svelte';
import { views } from '../../../stores';
import { randomString } from '../../../utils';
import { input } from '../../../actions';
export let collection;
export let show = false;
export let activeViewKey = 'list';
export let firstItem = {};
$: tabs = Object.entries($views).filter(v => (
v[0] === 'list' || (
v[1].host === collection.hostKey &&
v[1].database === collection.dbKey &&
v[1].collection === collection.key
)
)).sort((a, b) => sortTabKeys(a[0], b[0]))
.map(([ key, v ]) => ({ key, title: v.name, closable: key !== 'list' }));
function sortTabKeys(a, b) {
if (a === 'list') {
return -1;
}
if (b === 'list') {
return 1;
}
else {
return a.localeCompare(b);
}
}
function createView() {
const newViewKey = randomString();
$views[newViewKey] = {
name: 'Table view',
host: collection.hostKey,
database: collection.dbKey,
collection: collection.key,
type: 'table',
columns: [ { key: '_id' } ],
};
activeViewKey = newViewKey;
}
function removeView(viewKey) {
const keys = Object.keys($views).sort(sortTabKeys);
const oldIndex = keys.indexOf(viewKey);
const newKey = keys[oldIndex - 1];
console.log(keys, oldIndex, newKey);
activeViewKey = newKey;
delete $views[viewKey];
$views = $views;
}
function addColumn(before) {
if (typeof before === 'number') {
$views[activeViewKey].columns = [
...$views[activeViewKey].columns.slice(0, before),
{},
...$views[activeViewKey].columns.slice(before),
];
}
else {
$views[activeViewKey].columns = [ ...$views[activeViewKey].columns, {} ];
}
}
function addSuggestedColumns() {
if ((typeof firstItem !== 'object') || (firstItem === null)) {
return;
}
$views[activeViewKey].columns = Object.keys(firstItem).map(key => ({ key }));
}
function moveColumn(oldIndex, delta) {
const column = $views[activeViewKey].columns[oldIndex];
const newIndex = oldIndex + delta;
$views[activeViewKey].columns.splice(oldIndex, 1);
$views[activeViewKey].columns.splice(newIndex, 0, column);
$views[activeViewKey].columns = $views[activeViewKey].columns;
}
function removeColumn(index) {
$views[activeViewKey].columns.splice(index, 1);
$views[activeViewKey].columns = $views[activeViewKey].columns;
}
</script>
<Modal title="View configuration" bind:show contentPadding={false}>
<TabBar
{tabs}
canAddTab={true}
on:addTab={createView}
on:closeTab={e => removeView(e.detail)}
bind:selectedKey={activeViewKey}
/>
<div class="options">
{#if $views[activeViewKey]}
<div class="meta">
{#key activeViewKey}
<label class="field">
<span class="label">View name</span>
<input type="text" use:input={{ autofocus: true }} bind:value={$views[activeViewKey].name} disabled={activeViewKey === 'list'} />
</label>
{/key}
<label class="field">
<span class="label">View type</span>
<select bind:value={$views[activeViewKey].type} disabled={activeViewKey === 'list'}>
<option value="list">List view</option>
<option value="table">Table view</option>
</select>
</label>
</div>
{#if $views[activeViewKey].type === 'list'}
<div class="flex">
<input type="checkbox" id="hideObjectIndicators" bind:checked={$views[activeViewKey].hideObjectIndicators} />
<label for="hideObjectIndicators">
Hide object indicators ({'{...}'} and [...]) in list view and show nothing instead
</label>
</div>
{:else if $views[activeViewKey].type === 'table'}
{#each $views[activeViewKey].columns as column, columnIndex}
<div class="column">
<label class="field">
<input type="text" use:input bind:value={column.key} placeholder="Column keypath" />
</label>
<button class="btn" type="button" on:click={() => addColumn(columnIndex)} title="Add column before this one">
<Icon name="+" />
</button>
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, -1)} disabled={columnIndex === 0} title="Move column one position up">
<Icon name="chev-u" />
</button>
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, 1)} disabled={columnIndex === $views[activeViewKey].columns.length - 1} title="Move column one position down">
<Icon name="chev-d" />
</button>
<button class="btn danger" type="button" on:click={() => removeColumn(columnIndex)} title="Remove this column">
<Icon name="x" />
</button>
</div>
{/each}
<button class="btn" on:click={addColumn}>
<Icon name="+" /> Add column
</button>
<button class="btn" on:click={addSuggestedColumns} disabled={!firstItem}>
<Icon name="zap" /> Add suggested columns
</button>
{/if}
{/if}
</div>
</Modal>
<style>
.options {
padding: 1rem;
}
.meta {
display: grid;
grid-template: 1fr / 1fr 1fr;
gap: 0.5rem;
margin-bottom: 1rem;
}
.flex {
display: flex;
gap: 0.5rem;
align-items: center;
}
.column {
display: grid;
grid-template: 1fr / 1fr repeat(4, auto);
gap: 0.5rem;
margin-bottom: 0.5rem;
}
</style>

View File

@ -1,18 +1,18 @@
<script>
import { FindItems, RemoveItemById } from '../../../../wailsjs/go/app/App';
import CodeExample from '../../../components/code-example.svelte';
import { input } from '../../../actions';
import ObjectGrid from '../../../components/objectgrid.svelte';
import Icon from '../../../components/icon.svelte';
import ObjectViewer from '../../../components/objectviewer.svelte';
import FindViewConfigModal from './find-viewconfig.svelte';
import { onMount } from 'svelte';
import Grid from '../../../components/grid.svelte';
import { applicationSettings, views } from '../../../stores';
import { EJSON } from 'bson';
import { createEventDispatcher, onMount } from 'svelte';
import { FindItems, RemoveItemById } from '../../../../wailsjs/go/app/App';
import { input } from '../../../actions';
import CodeExample from '../../../components/code-example.svelte';
import Grid from '../../../components/grid.svelte';
import Icon from '../../../components/icon.svelte';
import ObjectGrid from '../../../components/objectgrid.svelte';
import ObjectViewer from '../../../components/objectviewer.svelte';
import { applicationSettings, views } from '../../../stores';
export let collection;
const dispatch = createEventDispatcher();
const defaults = {
query: '{}',
sort: $applicationSettings.defaultSort || '{ "_id": 1 }',
@ -22,19 +22,16 @@
};
let form = { ...defaults };
let activeViewKey = 'list';
let result = {};
let submittedForm = {};
let queryField;
let activePath = [];
let objectViewerData;
let viewConfigModalOpen = false;
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
$: 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})` : ''};`;
$: lastPage = (submittedForm.limit && result?.results?.length) ? Math.max(0, Math.ceil((result.total - submittedForm.limit) / submittedForm.limit)) : 0;
$: activePage = (submittedForm.limit && submittedForm.skip && result?.results?.length) ? submittedForm.skip / submittedForm.limit : 0;
$: collection && refresh();
async function submitQuery() {
activePath = [];
const newResult = await FindItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
@ -100,6 +97,7 @@
submitQuery();
}
$: collection && refresh();
onMount(refresh);
</script>
@ -108,19 +106,19 @@
<div class="form-row one">
<label class="field">
<span class="label">Query or id</span>
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ json: true, autofocus: true }} placeholder={defaults.query} />
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ type: 'json', autofocus: true }} placeholder={defaults.query} />
</label>
<label class="field">
<span class="label">Sort</span>
<input type="text" class="code" bind:value={form.sort} use:input={{ json: true }} placeholder={defaults.sort} />
<input type="text" class="code" bind:value={form.sort} use:input={{ type: 'json' }} placeholder={defaults.sort} />
</label>
</div>
<div class="form-row two">
<label class="field">
<span class="label">Fields</span>
<input type="text" class="code" bind:value={form.fields} use:input={{ json: true }} placeholder={defaults.fields} />
<input type="text" class="code" bind:value={form.fields} use:input={{ type: 'json' }} placeholder={defaults.fields} />
</label>
<label class="field">
@ -142,19 +140,19 @@
<div class="result">
<div class="grid">
{#key result}
{#if activeViewKey === 'table'}
<Grid
key="_id"
columns={$views[activeViewKey]?.columns?.map(c => ({ key: c.key, title: c.key })) || []}
showHeaders={true}
items={result.results || []}
{#if collection.viewKey === 'list'}
<ObjectGrid
data={result.results}
hideObjectIndicators={$views[collection.viewKey]?.hideObjectIndicators}
bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)}
/>
{:else if activeViewKey === 'list'}
<ObjectGrid
data={result.results}
hideObjectIndicators={$views[activeViewKey]?.hideObjectIndicators}
{:else}
<Grid
key="_id"
columns={$views[collection.viewKey]?.columns?.map(c => ({ key: c.key, title: c.key })) || []}
showHeaders={true}
items={result.results ? result.results.map(r => EJSON.deserialize(r)) : []}
bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)}
/>
@ -169,9 +167,16 @@
{/key}
</div>
<div>
<button class="btn" on:click={() => viewConfigModalOpen = true} title="Configure view">
<Icon name="cog" />
</button>
<label class="field inline">
<select bind:value={collection.viewKey}>
{#each Object.entries(viewsForCollection) as [key, view]}
<option value={key}>{view.name}</option>
{/each}
</select>
<button class="btn" on:click={() => dispatch('openViewConfig', { firstItem: result.results?.[0] })} title="Configure view">
<Icon name="cog" />
</button>
</label>
<button class="btn danger" on:click={removeActive} disabled={!activePath?.length} title="Drop selected item">
<Icon name="-" />
</button>
@ -193,12 +198,6 @@
</div>
<ObjectViewer bind:data={objectViewerData} />
<FindViewConfigModal
bind:show={viewConfigModalOpen}
bind:activeViewKey
firstItem={result.results?.[0]}
{collection}
/>
<datalist id="limits">
{#each [ 1, 5, 10, 25, 50, 100, 200 ] as value}

View File

@ -9,6 +9,7 @@
import Stats from './stats.svelte';
import Update from './update.svelte';
import { EventsOn } from '../../../../wailsjs/runtime/runtime';
import ViewConfig from './components/viewconfig.svelte';
export let collection;
export let hostKey;
@ -17,6 +18,8 @@
let tab = 'find';
let find;
let viewConfigModalOpen = false;
let firstItem;
$: if (collection) {
collection.hostKey = hostKey;
@ -31,6 +34,11 @@
await tick();
find.performQuery(event.detail);
}
function openViewConfig(event) {
firstItem = event.detail?.firstItem;
viewConfigModalOpen = true;
}
</script>
<div class="collection" class:empty={!collection}>
@ -47,8 +55,8 @@
<div class="container">
{#if tab === 'stats'} <Stats {collection} />
{:else if tab === 'find'} <Find {collection} bind:this={find} />
{:else if tab === 'insert'} <Insert {collection} on:performFind={catchQuery} />
{:else if tab === 'find'} <Find {collection} bind:this={find} on:openViewConfig={openViewConfig} />
{:else if tab === 'insert'} <Insert {collection} on:performFind={catchQuery} on:openViewConfig={openViewConfig} />
{:else if tab === 'update'} <Update {collection} on:performFind={catchQuery} />
{:else if tab === 'remove'} <Remove {collection} />
{:else if tab === 'indexes'} <Indexes {collection} />
@ -60,6 +68,15 @@
{/if}
</div>
{#if collection}
<ViewConfig
bind:show={viewConfigModalOpen}
bind:activeViewKey={collection.viewKey}
{firstItem}
{collection}
/>
{/if}
<style>
.collection {
height: 100%;

View File

@ -3,7 +3,7 @@
import ObjectGrid from '../../../components/objectgrid.svelte';
import { DropIndex, GetIndexes } from '../../../../wailsjs/go/app/App';
import Icon from '../../../components/icon.svelte';
import IndexDetail from './indexes-detail.svelte';
import IndexDetail from './components/indexdetail.svelte';
export let collection;

View File

@ -1,17 +1,34 @@
<script>
import { input } from '../../../actions';
import { views } from '../../../stores';
import { createEventDispatcher } from 'svelte';
import { InsertItems } from '../../../../wailsjs/go/app/App';
import { input } from '../../../actions';
import Icon from '../../../components/icon.svelte';
import Form from './components/form.svelte';
import ObjectViewer from '../../../components/objectviewer.svelte';
export let collection;
const dispatch = createEventDispatcher();
let json = '';
let newItems = [];
let insertedIds;
let objectViewerData = '';
let viewType = 'form';
let formValid = false;
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
$: oppositeViewType = viewType === 'table' ? 'form' : 'table';
async function insert() {
insertedIds = await InsertItems(collection.hostKey, collection.dbKey, collection.key, json);
if (collection.viewKey === 'list') {
insertedIds = await InsertItems(collection.hostKey, collection.dbKey, collection.key, json);
}
else {
insertedIds = await InsertItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(newItems));
if (insertedIds) {
newItems = [];
}
}
}
function showDocs() {
@ -21,19 +38,38 @@
: `{ "_id": { "$in": [ ${insertedIds.map(id => JSON.stringify(id)).join(', ')} ] } }`,
});
}
function switchViewType() {
viewType = oppositeViewType;
}
function showJson() {
if (viewType === 'form') {
objectViewerData = { ...(newItems[0] || {}) };
}
else if (viewType === 'table') {
objectViewerData = [ ...newItems ];
}
}
</script>
<form on:submit|preventDefault={insert}>
<label class="field">
<textarea
cols="30"
rows="10"
placeholder="[]"
class="code"
bind:value={json}
use:input={{ json: true, autofocus: true }}
></textarea>
</label>
{#if collection.viewKey === 'list'}
<label class="field">
<textarea
cols="30"
rows="10"
placeholder="[]"
class="code"
bind:value={json}
use:input={{ type: 'json', autofocus: true }}
></textarea>
</label>
{:else}
<div class="form">
<Form bind:item={newItems[0]} bind:valid={formValid} view={$views[collection.viewKey]} />
</div>
{/if}
<div class="flex">
<div>
@ -45,13 +81,33 @@
{#if insertedIds}
<button class="btn" type="button" on:click={showDocs}>View inserted docs</button>
{/if}
<button type="submit" class="btn" disabled={!json}>
{#if collection.viewKey !== 'list'}
<button class="btn" type="button" on:click={showJson} title="Show JSON">
<Icon name="code" />
</button>
<button class="btn" type="button" on:click={switchViewType} title="Edit as {oppositeViewType}">
<Icon name={oppositeViewType} />
</button>
{/if}
<label class="field inline">
<select bind:value={collection.viewKey}>
{#each Object.entries(viewsForCollection) as [key, view]}
<option value={key}>{key === 'list' ? 'Raw JSON' : view.name}</option>
{/each}
</select>
<button class="btn" type="button" on:click={() => dispatch('openViewConfig')} title="Configure view">
<Icon name="cog" />
</button>
</label>
<button type="submit" class="btn" disabled={$views[collection.viewKey]?.type === 'list' ? !json : !formValid}>
<Icon name="+" /> Insert
</button>
</div>
</div>
</form>
<ObjectViewer data={objectViewerData} />
<style>
form {
display: grid;

View File

@ -34,7 +34,7 @@
placeholder={'{}'}
class="code"
bind:value={json}
use:input={{ json: true, autofocus: true }}
use:input={{ type: 'json', autofocus: true }}
></textarea>
</label>

View File

@ -124,14 +124,14 @@
<label class="field">
<span class="label">Filter</span>
<input type="text" class="code" bind:value={form.query} use:input={{ json: true, autofocus: true }} placeholder={'{}'} />
<input type="text" class="code" bind:value={form.query} use:input={{ type: 'json', autofocus: true }} placeholder={'{}'} />
</label>
<fieldset class="parameters">
{#each form.parameters as param, index}
<fieldset class="parameter">
<label class="field">
<select bind:value={param.type}>
<select bind:value={param.type} class="type">
{#each Object.entries(atomicUpdateOperators) as [groupName, options]}
<optgroup label={groupName}>
{#each Object.entries(options) as [key, label]}
@ -142,18 +142,17 @@
</optgroup>
{/each}
</select>
<input type="text" class="code" bind:value={param.value} placeholder={'{}'} use:input={{ type: 'json' }} />
</label>
<label class="field">
<input type="text" class="code" bind:value={param.value} placeholder={'{}'} use:input={{ json: true }} />
<button class="btn" disabled={form.parameters.length >= allOperators.length} on:click={() => addParameter()} type="button">
<Icon name="+" />
</button>
<button class="btn" disabled={form.parameters.length < 2} on:click={() => removeParam(index)} type="button">
<Icon name="-" />
</button>
</label>
<button class="btn" disabled={form.parameters.length >= allOperators.length} on:click={() => addParameter()} type="button">
<Icon name="+" />
</button>
<button class="btn" disabled={form.parameters.length < 2} on:click={() => removeParam(index)} type="button">
<Icon name="-" />
</button>
</fieldset>
{/each}
</fieldset>
@ -182,7 +181,11 @@
}
.parameter {
display: grid;
grid-template: 1fr / auto 1fr auto auto;
grid-template: 1fr / 1fr auto;
gap: 0.5rem;
}
select.type {
max-width: 150px;
}
</style>

View File

@ -7,8 +7,6 @@
export let info;
export let hosts = {};
$: console.log(info);
async function selectHost(hostKey) {
info.hostKey = hostKey;
info.dbKey = undefined;