1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-01-18 13:07:58 +00:00

Context menu + database dropping

This commit is contained in:
Romein van Buren 2023-01-14 20:38:39 +01:00
parent 5a558577ea
commit b7365b739c
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
10 changed files with 182 additions and 19 deletions

View File

@ -1,10 +1,11 @@
<script>
import { onMount } from 'svelte';
import { Hosts, OpenCollection, OpenConnection, OpenDatabase } from '../wailsjs/go/main/App';
import { DropDatabase, Hosts, OpenCollection, OpenConnection, OpenDatabase } from '../wailsjs/go/main/App';
import AddressBar from './organisms/addressbar/index.svelte';
import Grid from './components/grid.svelte';
import CollectionDetail from './organisms/collection-detail/index.svelte';
import { busy } from './stores';
import { busy, contextMenu } from './stores';
import ContextMenu from './components/contextmenu.svelte';
const connections = {};
let hosts = {};
@ -46,6 +47,13 @@
$busy = false;
}
async function dropDatabase(dbKey) {
$busy = true;
await DropDatabase(activeHostKey, dbKey);
await openConnection(activeHostKey);
$busy = false();
}
async function openCollection(collKey) {
$busy = true;
const stats = await OpenCollection(activeHostKey, activeDbKey, collKey);
@ -53,6 +61,11 @@
$busy = false;
}
async function reload() {
activeHostKey && await openConnection(activeHostKey);
activeDbKey && await openDatabase(activeDbKey);
}
onMount(() => {
Hosts().then(h => hosts = h);
});
@ -62,14 +75,20 @@
<AddressBar {hosts} bind:activeHostKey on:select={e => openConnection(e.detail)} bind:modalOpen={addressBarModalOpen} />
{#if host && connection}
<div class="hostlist">
<div class="databaselist">
<Grid
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
items={Object.keys(connection.databases).map(id => ({
id,
collCount: Object.keys(connection.databases[id].collections || {}).length,
children: connection.databases[id].collections || [],
menu: [ { label: `Drop ${id}`, fn: () => dropDatabase(id) } ],
}))}
actions={[
{ icon: 'reload', fn: reload },
{ icon: '+' },
{ icon: '-' },
]}
bind:activeKey={activeDbKey}
bind:activeChildKey={activeCollKey}
on:select={e => openDatabase(e.detail)}
@ -88,6 +107,10 @@
{/if}
</main>
{#key $contextMenu}
<ContextMenu {...$contextMenu} on:close={contextMenu.hide} />
{/key}
<style>
main {
height: 100vh;
@ -100,7 +123,7 @@
grid-column: 1 / 3;
}
.hostlist {
.databaselist {
overflow: scroll;
}
</style>

View File

@ -0,0 +1,58 @@
<script>
import { createEventDispatcher } from 'svelte';
export let items = undefined;
export let position = undefined;
const dispatch = createEventDispatcher();
function close() {
dispatch('close');
}
</script>
{#if items && position}
<div class="backdrop" on:pointerdown={close}></div>
<ul class="contextmenu" role="" style:left="{position[0]}px" style:top="{position[1]}px">
{#each items as item}
{#if item.separator}
<hr />
{:else}
<li>
<button class="item" on:click={item.fn}>
{item.label}
</button>
</li>
{/if}
{/each}
</ul>
{/if}
<style>
.backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.contextmenu {
position: fixed;
background-color: rgba(230, 230, 230, 0.7);
backdrop-filter: blur(30px);
border-radius: 10px;
padding: 5px;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.2);
}
button {
padding: 5px;
border-radius: 5px;
}
button:hover {
background-color: #00008b;
color: #fff;
}
</style>

View File

@ -1,9 +1,11 @@
<script>
import { contextMenu } from '../stores';
import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte';
export let columns = [];
export let items = [];
export let actions = [];
export let key = 'id';
export let activeKey = '';
export let activeChildKey = '';
@ -12,7 +14,7 @@
export let contained = false;
const dispatch = createEventDispatcher();
const childrenOpen = {};
let childrenOpen = {};
$: _items = objectToArray(items).map(item => {
item.children = objectToArray(item.children);
@ -43,12 +45,32 @@
dispatch('selectChild', childKey);
}
function toggleChildren(itemKey) {
function toggleChildren(itemKey, closeAll) {
childrenOpen[itemKey] = !childrenOpen[itemKey];
if (closeAll) {
childrenOpen = {};
dispatch('closeAll');
}
}
function showContextMenu(evt, item) {
select(item[key]);
contextMenu.show(evt, item.menu);
}
</script>
<div class:grid={level === 0} class:subgrid={level > 0} class:contained>
{#if actions?.length}
<div class="actions">
{#each actions as action}
<button class="btn" on:click={action.fn}>
{#if action.icon}<Icon name={action.icon} />{/if}
{action.label || ''}
</button>
{/each}
</div>
{/if}
<table>
{#if showHeaders && columns.some(col => col.title)}
<thead>
@ -63,10 +85,15 @@
<tbody>
{#each _items as item (item[key])}
<tr on:click={() => select(item[key])} class:selected={activeKey === item[key] && !activeChildKey}>
<tr
on:click={() => select(item[key])}
on:dblclick={() => toggleChildren(item[key])}
on:contextmenu|preventDefault={evt => showContextMenu(evt, item)}
class:selected={activeKey === item[key] && !activeChildKey}
>
<td class="has-toggle">
{#if item.children?.length}
<button class="toggle" on:click={() => toggleChildren(item[key])}>
<button class="toggle" on:click={evt => toggleChildren(item[key], evt.shiftKey)}>
<Icon name={childrenOpen[item[key]] ? 'chev-d' : 'chev-r'} />
</button>
{/if}
@ -94,6 +121,7 @@
items={item.children}
level={level + 1}
on:select={e => selectChild(item[key], e.detail)}
on:closeAll={() => (childrenOpen = {})}
/>
</td>
</tr>
@ -116,6 +144,15 @@
width: 100%;
}
.actions {
margin-bottom: 0.5rem;
padding: 0.5rem;
border-bottom: 1px solid #ccc;
}
.actions button {
margin-right: 0.2rem;
}
table {
border-collapse: collapse;
width: 100%;

View File

@ -1,5 +1,5 @@
<script>
export let name;
export let name = '';
</script>
{#if name === 'radio'}
@ -14,6 +14,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-database"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
{:else if name === 'x'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
{:else if name === '+'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
{:else if name === '-'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-minus"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
{:else if name === 'reload'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>
{/if}

View File

@ -13,17 +13,10 @@
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
};
let form = {
query: '{}',
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
limit: 15,
};
let form = { ...defaults };
let result = {};
let submittedForm = {};
let queryField;

View File

@ -24,7 +24,6 @@
async function catchQuery(event) {
tab = 'find';
await tick();
console.log(event, find);
find.performQuery(event.detail);
}
</script>

View File

@ -9,3 +9,15 @@ busy.subscribe(isBusy => {
document.body.classList.remove('busy');
}
});
export const contextMenu = (() => {
const { set, subscribe } = writable();
return {
show: (evt, menu) => set(menu ? {
position: [ evt.clientX, evt.clientY ],
items: menu,
} : undefined),
hide: () => set(undefined),
subscribe,
};
})();

View File

@ -4,6 +4,8 @@ import {map[string]main} from '../models';
import {primitive} from '../models';
import {main} from '../models';
export function DropDatabase(arg1:string,arg2:string):Promise<boolean>;
export function Hosts():Promise<map[string]main.Host>;
export function OpenCollection(arg1:string,arg2:string,arg3:string):Promise<primitive.M>;

View File

@ -2,6 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function DropDatabase(arg1, arg2) {
return window['go']['main']['App']['DropDatabase'](arg1, arg2);
}
export function Hosts() {
return window['go']['main']['App']['Hosts']();
}

31
main.go
View File

@ -120,6 +120,37 @@ func (a *App) OpenDatabase(hostKey, dbKey string) (collections []string) {
return collections
}
func (a *App) DropDatabase(hostKey, dbKey string) bool {
sure, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Title: "Confirm",
Message: "Are you sure you want to drop " + dbKey + "?",
Buttons: []string{"yes", "no"},
DefaultButton: "Yes",
CancelButton: "No",
})
if sure != "Yes" {
return false
}
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return false
}
err = client.Database(dbKey).Drop(ctx)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Could not drop " + dbKey,
Message: err.Error(),
})
return false
}
defer close()
return true
}
func (a *App) OpenCollection(hostKey, dbKey, collKey string) (result bson.M) {
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {