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:
parent
5a558577ea
commit
b7365b739c
@ -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>
|
||||
|
58
frontend/src/components/contextmenu.svelte
Normal file
58
frontend/src/components/contextmenu.svelte
Normal 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>
|
@ -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%;
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -24,7 +24,6 @@
|
||||
async function catchQuery(event) {
|
||||
tab = 'find';
|
||||
await tick();
|
||||
console.log(event, find);
|
||||
find.performQuery(event.detail);
|
||||
}
|
||||
</script>
|
||||
|
@ -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,
|
||||
};
|
||||
})();
|
||||
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -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>;
|
||||
|
@ -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
31
main.go
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user