mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 01:33:59 +00:00
Merge branch 'feature/connection-keyboard-browse'
This commit is contained in:
@@ -50,7 +50,7 @@
|
|||||||
<svelte:component
|
<svelte:component
|
||||||
this={module.default}
|
this={module.default}
|
||||||
{data}
|
{data}
|
||||||
on:click={handleExpand}
|
on:dblclick={handleExpand}
|
||||||
on:expand={handleExpandButton}
|
on:expand={handleExpandButton}
|
||||||
expandIcon={getExpandIcon(!isExpandedBySearch && expandable, subItemsComponent, isExpanded, expandIconFunc)}
|
expandIcon={getExpandIcon(!isExpandedBySearch && expandable, subItemsComponent, isExpanded, expandIconFunc)}
|
||||||
{checkedObjectsStore}
|
{checkedObjectsStore}
|
||||||
|
|||||||
@@ -12,6 +12,16 @@
|
|||||||
return filterName(filter, ...databases.map(x => x.name));
|
return filterName(filter, ...databases.map(x => x.name));
|
||||||
};
|
};
|
||||||
export function openConnection(connection) {
|
export function openConnection(connection) {
|
||||||
|
if (connection.singleDatabase) {
|
||||||
|
if (getOpenedSingleDatabaseConnections().includes(connection._id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (getOpenedConnections().includes(connection._id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const config = getCurrentConfig();
|
const config = getCurrentConfig();
|
||||||
if (connection.singleDatabase) {
|
if (connection.singleDatabase) {
|
||||||
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
|
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
|
||||||
@@ -83,10 +93,12 @@
|
|||||||
currentDatabase,
|
currentDatabase,
|
||||||
expandedConnections,
|
expandedConnections,
|
||||||
extensions,
|
extensions,
|
||||||
|
focusedConnectionOrDatabase,
|
||||||
getCurrentConfig,
|
getCurrentConfig,
|
||||||
getCurrentDatabase,
|
getCurrentDatabase,
|
||||||
getCurrentSettings,
|
getCurrentSettings,
|
||||||
getOpenedConnections,
|
getOpenedConnections,
|
||||||
|
getOpenedSingleDatabaseConnections,
|
||||||
getOpenedTabs,
|
getOpenedTabs,
|
||||||
openedConnections,
|
openedConnections,
|
||||||
openedSingleDatabaseConnections,
|
openedSingleDatabaseConnections,
|
||||||
@@ -135,7 +147,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = async () => {
|
const handleDoubleClick = async () => {
|
||||||
const config = getCurrentConfig();
|
const config = getCurrentConfig();
|
||||||
if (config.runAsPortal) {
|
if (config.runAsPortal) {
|
||||||
await tick();
|
await tick();
|
||||||
@@ -158,6 +170,19 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClick = async e => {
|
||||||
|
focusedConnectionOrDatabase.set({ conid: data?._id });
|
||||||
|
openNewTab({
|
||||||
|
title: getConnectionLabel(data),
|
||||||
|
icon: 'img connection',
|
||||||
|
tabComponent: 'ConnectionTab',
|
||||||
|
tabPreviewMode: true,
|
||||||
|
props: {
|
||||||
|
conid: data._id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleSqlRestore = () => {
|
const handleSqlRestore = () => {
|
||||||
showModal(ImportDatabaseDumpModal, {
|
showModal(ImportDatabaseDumpModal, {
|
||||||
connection: data,
|
connection: data,
|
||||||
@@ -329,7 +354,7 @@
|
|||||||
colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })}
|
colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })}
|
||||||
menu={getContextMenu}
|
menu={getContextMenu}
|
||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
on:click
|
on:dblclick
|
||||||
on:expand
|
on:expand
|
||||||
on:dblclick={handleConnect}
|
on:dblclick={handleConnect}
|
||||||
on:middleclick={() => {
|
on:middleclick={() => {
|
||||||
@@ -337,4 +362,5 @@
|
|||||||
.find(x => x.isNewQuery)
|
.find(x => x.isNewQuery)
|
||||||
.onClick();
|
.onClick();
|
||||||
}}
|
}}
|
||||||
|
isChoosed={data._id == $focusedConnectionOrDatabase?.conid && !$focusedConnectionOrDatabase?.database}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -446,6 +446,7 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
|||||||
currentArchive,
|
currentArchive,
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
extensions,
|
extensions,
|
||||||
|
focusedConnectionOrDatabase,
|
||||||
getCurrentDatabase,
|
getCurrentDatabase,
|
||||||
getExtensions,
|
getExtensions,
|
||||||
getOpenedTabs,
|
getOpenedTabs,
|
||||||
@@ -512,7 +513,14 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
|||||||
passProps?.connectionColorFactory({ conid: data?.connection?._id, database: data.name }, null, null, false)}
|
passProps?.connectionColorFactory({ conid: data?.connection?._id, database: data.name }, null, null, false)}
|
||||||
isBold={$currentDatabase?.connection?._id == data?.connection?._id &&
|
isBold={$currentDatabase?.connection?._id == data?.connection?._id &&
|
||||||
extractDbNameFromComposite($currentDatabase?.name) == data.name}
|
extractDbNameFromComposite($currentDatabase?.name) == data.name}
|
||||||
on:click={() => switchCurrentDatabase(data)}
|
on:dblclick={() => {
|
||||||
|
switchCurrentDatabase(data);
|
||||||
|
// passProps?.onFocusSqlObjectList?.();
|
||||||
|
}}
|
||||||
|
on:click={() => {
|
||||||
|
// switchCurrentDatabase(data);
|
||||||
|
$focusedConnectionOrDatabase = { conid: data.connection?._id, database: data.name, connection: data.connection };
|
||||||
|
}}
|
||||||
on:dragstart
|
on:dragstart
|
||||||
on:dragenter
|
on:dragenter
|
||||||
on:dragend
|
on:dragend
|
||||||
@@ -532,4 +540,6 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
|||||||
list.filter(x => x?.name != data?.name || x?.connection?._id != data?.connection?._id)
|
list.filter(x => x?.name != data?.name || x?.connection?._id != data?.connection?._id)
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
|
isChoosed={data.connection?._id == $focusedConnectionOrDatabase?.conid &&
|
||||||
|
data.name == $focusedConnectionOrDatabase?.database}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,13 +15,11 @@
|
|||||||
|
|
||||||
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
|
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
|
||||||
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
|
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
|
||||||
|
|
||||||
$: connectionLabel = getConnectionLabel(data);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
list={_.sortBy(
|
list={_.sortBy(
|
||||||
dbList.filter(x => filterName(filter, x.name, connectionLabel)),
|
dbList.filter(x => filterName(filter, x.name, data.displayName, data.server)),
|
||||||
x => x.sortOrder ?? x.name
|
x => x.sortOrder ?? x.name
|
||||||
).map(db => ({ ...db, connection: data }))}
|
).map(db => ({ ...db, connection: data }))}
|
||||||
module={databaseAppObject}
|
module={databaseAppObject}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let value;
|
export let value;
|
||||||
export let title = null;
|
export let title = null;
|
||||||
|
export let skipWidth = false;
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
if (!disabled) dispatch('click');
|
if (!disabled) dispatch('click');
|
||||||
@@ -19,19 +20,22 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input {type} {value} {title} class:disabled {...$$restProps} on:click={handleClick} bind:this={domButton} />
|
<input {type} {value} {title} class:disabled {...$$restProps} on:click={handleClick} bind:this={domButton} class:skipWidth />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
input {
|
input {
|
||||||
border: 1px solid var(--theme-bg-button-inv-2);
|
border: 1px solid var(--theme-bg-button-inv-2);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
width: 100px;
|
|
||||||
background-color: var(--theme-bg-button-inv);
|
background-color: var(--theme-bg-button-inv);
|
||||||
color: var(--theme-font-inv-1);
|
color: var(--theme-font-inv-1);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:not(.skipWidth) {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
input:hover:not(.disabled) {
|
input:hover:not(.disabled) {
|
||||||
background-color: var(--theme-bg-button-inv-2);
|
background-color: var(--theme-bg-button-inv-2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,12 @@
|
|||||||
|
|
||||||
const debouncedSet = _.debounce(x => (value = x), 500);
|
const debouncedSet = _.debounce(x => (value = x), 500);
|
||||||
|
|
||||||
export function focus() {
|
export function focus(text) {
|
||||||
domInput.focus();
|
domInput.focus();
|
||||||
|
if (text) {
|
||||||
|
domInput.value = text;
|
||||||
|
value = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ export const appliedCurrentSchema = writable<string>(null);
|
|||||||
export const loadingSchemaLists = writable({}); // dict [`${conid}::${database}`]: true
|
export const loadingSchemaLists = writable({}); // dict [`${conid}::${database}`]: true
|
||||||
|
|
||||||
export const selectedDatabaseObjectAppObject = writable(null);
|
export const selectedDatabaseObjectAppObject = writable(null);
|
||||||
|
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
||||||
|
|
||||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||||
@@ -339,3 +340,15 @@ openedModals.subscribe(value => {
|
|||||||
openedModalsValue = value;
|
openedModalsValue = value;
|
||||||
});
|
});
|
||||||
export const getOpenedModals = () => openedModalsValue;
|
export const getOpenedModals = () => openedModalsValue;
|
||||||
|
|
||||||
|
let focusedConnectionOrDatabaseValue = null;
|
||||||
|
focusedConnectionOrDatabase.subscribe(value => {
|
||||||
|
focusedConnectionOrDatabaseValue = value;
|
||||||
|
});
|
||||||
|
export const getFocusedConnectionOrDatabase = () => focusedConnectionOrDatabaseValue;
|
||||||
|
|
||||||
|
let openedSingleDatabaseConnectionsValue = [];
|
||||||
|
openedSingleDatabaseConnections.subscribe(value => {
|
||||||
|
openedSingleDatabaseConnectionsValue = value;
|
||||||
|
});
|
||||||
|
export const getOpenedSingleDatabaseConnections = () => openedSingleDatabaseConnectionsValue;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
|
focusedConnectionOrDatabase,
|
||||||
getActiveTab,
|
getActiveTab,
|
||||||
getCurrentDatabase,
|
getCurrentDatabase,
|
||||||
getLockedDatabaseMode,
|
getLockedDatabaseMode,
|
||||||
@@ -78,6 +79,16 @@ export async function handleAfterTabClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentDatabase.subscribe(currentDb => {
|
currentDatabase.subscribe(currentDb => {
|
||||||
|
if (currentDb) {
|
||||||
|
focusedConnectionOrDatabase.set({
|
||||||
|
conid: currentDb.connection?._id,
|
||||||
|
database: currentDb.name,
|
||||||
|
connection: currentDb.connection,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
focusedConnectionOrDatabase.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (!getLockedDatabaseMode()) return;
|
if (!getLockedDatabaseMode()) return;
|
||||||
if (!currentDb && !getAppLoaded()) return;
|
if (!currentDb && !getAppLoaded()) return;
|
||||||
openedTabs.update(tabs => {
|
openedTabs.update(tabs => {
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { sleep } from '../utility/common';
|
||||||
|
|
||||||
export let list;
|
export let list;
|
||||||
export let selectedObjectStore;
|
export let selectedObjectStore;
|
||||||
export let getSelectedObject;
|
export let getSelectedObject;
|
||||||
export let selectedObjectMatcher;
|
export let selectedObjectMatcher;
|
||||||
export let module;
|
export let handleObjectClick;
|
||||||
|
|
||||||
export let onScrollTop = null;
|
export let onScrollTop = null;
|
||||||
export let onFocusFilterBox = null;
|
export let onFocusFilterBox = null;
|
||||||
|
export let getDefaultFocusedItem = null;
|
||||||
|
|
||||||
let isListFocused = false;
|
let isListFocused = false;
|
||||||
let domDiv = null;
|
let domDiv = null;
|
||||||
|
export let hideContent = false;
|
||||||
|
|
||||||
function handleKeyDown(ev) {
|
function handleKeyDown(ev) {
|
||||||
|
const listInstance = _.isFunction(list) ? list() : list;
|
||||||
|
|
||||||
function selectByDiff(diff) {
|
function selectByDiff(diff) {
|
||||||
const selected = getSelectedObject();
|
const selected = getSelectedObject();
|
||||||
const index = _.findIndex(list, x => selectedObjectMatcher(x, selected));
|
const index = _.findIndex(listInstance, x => selectedObjectMatcher(x, selected));
|
||||||
|
|
||||||
if (index == 0 && diff < 0) {
|
if (index == 0 && diff < 0) {
|
||||||
onFocusFilterBox?.();
|
onFocusFilterBox?.();
|
||||||
@@ -26,16 +31,16 @@
|
|||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
let newIndex = index + diff;
|
let newIndex = index + diff;
|
||||||
if (newIndex >= list.length) {
|
if (newIndex >= listInstance.length) {
|
||||||
newIndex = list.length - 1;
|
newIndex = listInstance.length - 1;
|
||||||
}
|
}
|
||||||
if (newIndex < 0) {
|
if (newIndex < 0) {
|
||||||
newIndex = 0;
|
newIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list[newIndex]) {
|
if (listInstance[newIndex]) {
|
||||||
selectedObjectStore.set(list[newIndex]);
|
selectedObjectStore.set(listInstance[newIndex]);
|
||||||
module.handleObjectClick(list[newIndex], { tabPreviewMode: true });
|
handleObjectClick?.(listInstance[newIndex], { tabPreviewMode: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newIndex == 0) {
|
if (newIndex == 0) {
|
||||||
@@ -52,7 +57,7 @@
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
if (ev.keyCode == keycodes.enter) {
|
if (ev.keyCode == keycodes.enter) {
|
||||||
module.handleObjectClick(getSelectedObject(), { tabPreviewMode: false, focusTab: true });
|
handleObjectClick?.(getSelectedObject(), { tabPreviewMode: false, focusTab: true });
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
if (ev.keyCode == keycodes.pageDown) {
|
if (ev.keyCode == keycodes.pageDown) {
|
||||||
@@ -64,28 +69,66 @@
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
if (ev.keyCode == keycodes.home) {
|
if (ev.keyCode == keycodes.home) {
|
||||||
if (list[0]) {
|
if (listInstance[0]) {
|
||||||
selectedObjectStore.set(list[0]);
|
selectedObjectStore.set(listInstance[0]);
|
||||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
handleObjectClick?.(listInstance[0], { tabPreviewMode: true });
|
||||||
onScrollTop?.();
|
onScrollTop?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ev.keyCode == keycodes.end) {
|
if (ev.keyCode == keycodes.end) {
|
||||||
if (list[list.length - 1]) {
|
if (listInstance[listInstance.length - 1]) {
|
||||||
selectedObjectStore.set(list[list.length - 1]);
|
selectedObjectStore.set(listInstance[listInstance.length - 1]);
|
||||||
module.handleObjectClick(list[list.length - 1], { tabPreviewMode: true });
|
handleObjectClick?.(listInstance[listInstance.length - 1], { tabPreviewMode: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!ev.ctrlKey &&
|
||||||
|
!ev.altKey &&
|
||||||
|
!ev.metaKey &&
|
||||||
|
((ev.keyCode >= keycodes.a && ev.keyCode <= keycodes.z) ||
|
||||||
|
(ev.keyCode >= keycodes.n0 && ev.keyCode <= keycodes.n9) ||
|
||||||
|
(ev.keyCode >= keycodes.numPad0 && ev.keyCode <= keycodes.numPad9) ||
|
||||||
|
ev.keyCode == keycodes.dash)
|
||||||
|
) {
|
||||||
|
const text = ev.key;
|
||||||
|
onFocusFilterBox?.(text);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function focusFirst() {
|
export function focusFirst() {
|
||||||
|
const listInstance = _.isFunction(list) ? list() : list;
|
||||||
|
|
||||||
domDiv?.focus();
|
domDiv?.focus();
|
||||||
if (list[0]) {
|
if (listInstance[0]) {
|
||||||
selectedObjectStore.set(list[0]);
|
selectedObjectStore.set(listInstance[0]);
|
||||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
handleObjectClick?.(listInstance[0], { tabPreviewMode: true });
|
||||||
onScrollTop?.();
|
onScrollTop?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleFocus() {
|
||||||
|
isListFocused = true;
|
||||||
|
// await tick();
|
||||||
|
await sleep(100);
|
||||||
|
// console.log('ON FOCUS AFTER SLEEP');
|
||||||
|
const listInstance = _.isFunction(list) ? list() : list;
|
||||||
|
const selected = getSelectedObject();
|
||||||
|
const index = _.findIndex(listInstance, x => selectedObjectMatcher(x, selected));
|
||||||
|
if (index < 0) {
|
||||||
|
const focused = getDefaultFocusedItem?.();
|
||||||
|
if (focused) {
|
||||||
|
const index2 = _.findIndex(listInstance, x => selectedObjectMatcher(x, focused));
|
||||||
|
if (index2 >= 0) {
|
||||||
|
selectedObjectStore.set(focused);
|
||||||
|
handleObjectClick?.(focused, { tabPreviewMode: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
focusFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -93,13 +136,12 @@
|
|||||||
on:keydown={handleKeyDown}
|
on:keydown={handleKeyDown}
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
class:app-object-list-focused={isListFocused}
|
class:app-object-list-focused={isListFocused}
|
||||||
on:focus={() => {
|
on:focus={handleFocus}
|
||||||
isListFocused = true;
|
|
||||||
}}
|
|
||||||
on:blur={() => {
|
on:blur={() => {
|
||||||
isListFocused = false;
|
isListFocused = false;
|
||||||
}}
|
}}
|
||||||
bind:this={domDiv}
|
bind:this={domDiv}
|
||||||
|
class:hideContent
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
@@ -108,4 +150,8 @@
|
|||||||
.wrapper:focus {
|
.wrapper:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hideContent {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
openedTabs,
|
openedTabs,
|
||||||
emptyConnectionGroupNames,
|
emptyConnectionGroupNames,
|
||||||
collapsedConnectionGroupNames,
|
collapsedConnectionGroupNames,
|
||||||
|
focusedConnectionOrDatabase,
|
||||||
|
getFocusedConnectionOrDatabase,
|
||||||
|
currentDatabase,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
import runCommand from '../commands/runCommand';
|
import runCommand from '../commands/runCommand';
|
||||||
import { getConnectionLabel } from 'dbgate-tools';
|
import { filterName, getConnectionLabel } from 'dbgate-tools';
|
||||||
import { useConnectionColorFactory } from '../utility/useConnectionColor';
|
import { useConnectionColorFactory } from '../utility/useConnectionColor';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||||
@@ -29,11 +32,21 @@
|
|||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
|
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
||||||
|
import { getLocalStorage } from '../utility/storageCache';
|
||||||
|
import { switchCurrentDatabase } from '../utility/common';
|
||||||
|
import openNewTab from '../utility/openNewTab';
|
||||||
|
import { openConnection } from '../appobj/ConnectionAppObject.svelte';
|
||||||
|
|
||||||
const connections = useConnectionList();
|
const connections = useConnectionList();
|
||||||
const serverStatus = useServerStatus();
|
const serverStatus = useServerStatus();
|
||||||
|
|
||||||
|
export let passProps: any = {};
|
||||||
|
|
||||||
let filter = '';
|
let filter = '';
|
||||||
|
let domListHandler;
|
||||||
|
let domContainer = null;
|
||||||
|
let domFilter = null;
|
||||||
|
|
||||||
$: connectionsWithStatus =
|
$: connectionsWithStatus =
|
||||||
$connections && $serverStatus
|
$connections && $serverStatus
|
||||||
@@ -47,12 +60,52 @@
|
|||||||
x => !x.unsaved || $openedConnections.includes(x._id) || $openedSingleDatabaseConnections.includes(x._id)
|
x => !x.unsaved || $openedConnections.includes(x._id) || $openedSingleDatabaseConnections.includes(x._id)
|
||||||
);
|
);
|
||||||
|
|
||||||
$: connectionsWithParent = connectionsWithStatusFiltered
|
$: connectionsWithParent = _.sortBy(
|
||||||
? connectionsWithStatusFiltered?.filter(x => x.parent !== undefined && x.parent !== null && x.parent.length !== 0)
|
connectionsWithStatusFiltered
|
||||||
: [];
|
? connectionsWithStatusFiltered?.filter(x => x.parent !== undefined && x.parent !== null && x.parent.length !== 0)
|
||||||
$: connectionsWithoutParent = connectionsWithStatusFiltered
|
: [],
|
||||||
? connectionsWithStatusFiltered?.filter(x => x.parent === undefined || x.parent === null || x.parent.length === 0)
|
connection => (getConnectionLabel(connection) || '').toUpperCase()
|
||||||
: [];
|
);
|
||||||
|
$: connectionsWithoutParent = _.sortBy(
|
||||||
|
connectionsWithStatusFiltered
|
||||||
|
? connectionsWithStatusFiltered?.filter(x => x.parent === undefined || x.parent === null || x.parent.length === 0)
|
||||||
|
: [],
|
||||||
|
connection => (getConnectionLabel(connection) || '').toUpperCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
function getFocusFlatList() {
|
||||||
|
const expanded = $expandedConnections;
|
||||||
|
const opened = $openedConnections;
|
||||||
|
|
||||||
|
const res = [];
|
||||||
|
for (const con of [...connectionsWithParent, ...connectionsWithoutParent]) {
|
||||||
|
const databases = getLocalStorage(`database_list_${con._id}`) || [];
|
||||||
|
if (!filterName(filter, con.displayName, con.server, ...databases.map(x => x.name))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
connection: con,
|
||||||
|
conid: con._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((expanded.includes(con._id) && opened.includes(con._id)) || filter) {
|
||||||
|
for (const db of _.sortBy(databases, x => x.sortOrder ?? x.name)) {
|
||||||
|
if (!filterName(filter, con.displayName, con.server, db.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
conid: con._id,
|
||||||
|
database: db.name,
|
||||||
|
connection: con,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
const handleRefreshConnections = () => {
|
const handleRefreshConnections = () => {
|
||||||
for (const conid of $openedConnections) {
|
for (const conid of $openedConnections) {
|
||||||
@@ -112,7 +165,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<SearchInput placeholder="Search connection or database" bind:value={filter} />
|
<SearchInput
|
||||||
|
placeholder="Search connection or database"
|
||||||
|
bind:value={filter}
|
||||||
|
bind:this={domFilter}
|
||||||
|
onFocusFilteredList={() => {
|
||||||
|
domListHandler?.focusFirst();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<CloseSearchButton bind:filter />
|
<CloseSearchButton bind:filter />
|
||||||
{#if $commandsCustomized['new.connection']?.enabled}
|
{#if $commandsCustomized['new.connection']?.enabled}
|
||||||
<InlineButton on:click={() => runCommand('new.connection')} title="Add new connection">
|
<InlineButton on:click={() => runCommand('new.connection')} title="Add new connection">
|
||||||
@@ -127,6 +187,7 @@
|
|||||||
</InlineButton>
|
</InlineButton>
|
||||||
</SearchBoxWrapper>
|
</SearchBoxWrapper>
|
||||||
<WidgetsInnerContainer
|
<WidgetsInnerContainer
|
||||||
|
bind:this={domContainer}
|
||||||
on:drop={e => {
|
on:drop={e => {
|
||||||
var data = e.dataTransfer.getData('app_object_drag_data');
|
var data = e.dataTransfer.getData('app_object_drag_data');
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -134,43 +195,88 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppObjectList
|
<AppObjectListHandler
|
||||||
list={_.sortBy(connectionsWithParent, connection => (getConnectionLabel(connection) || '').toUpperCase())}
|
bind:this={domListHandler}
|
||||||
module={connectionAppObject}
|
list={getFocusFlatList}
|
||||||
subItemsComponent={SubDatabaseList}
|
selectedObjectStore={focusedConnectionOrDatabase}
|
||||||
expandOnClick
|
getSelectedObject={getFocusedConnectionOrDatabase}
|
||||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
selectedObjectMatcher={(o1, o2) => o1?.conid == o2?.conid && o1?.database == o2?.database}
|
||||||
{filter}
|
getDefaultFocusedItem={() =>
|
||||||
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
$currentDatabase
|
||||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
? {
|
||||||
setIsExpanded={(data, value) => {
|
conid: $currentDatabase?.connection?._id,
|
||||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
database: $currentDatabase?.name,
|
||||||
|
connection: $currentDatabase?.connection,
|
||||||
|
}
|
||||||
|
: null}
|
||||||
|
onScrollTop={() => {
|
||||||
|
domContainer?.scrollTop();
|
||||||
}}
|
}}
|
||||||
groupIconFunc={chevronExpandIcon}
|
onFocusFilterBox={text => {
|
||||||
groupFunc={data => data.parent}
|
domFilter?.focus(text);
|
||||||
expandIconFunc={plusExpandIcon}
|
|
||||||
onDropOnGroup={handleDropOnGroup}
|
|
||||||
emptyGroupNames={$emptyConnectionGroupNames}
|
|
||||||
sortGroups
|
|
||||||
groupContextMenu={createGroupContextMenu}
|
|
||||||
collapsedGroupNames={collapsedConnectionGroupNames}
|
|
||||||
/>
|
|
||||||
{#if (connectionsWithParent?.length > 0 && connectionsWithoutParent?.length > 0) || ($emptyConnectionGroupNames.length > 0 && connectionsWithoutParent?.length > 0)}
|
|
||||||
<div class="br" />
|
|
||||||
{/if}
|
|
||||||
<AppObjectList
|
|
||||||
list={_.sortBy(connectionsWithoutParent, connection => (getConnectionLabel(connection) || '').toUpperCase())}
|
|
||||||
module={connectionAppObject}
|
|
||||||
subItemsComponent={SubDatabaseList}
|
|
||||||
expandOnClick
|
|
||||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
|
||||||
{filter}
|
|
||||||
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
|
||||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
|
||||||
setIsExpanded={(data, value) => {
|
|
||||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
|
||||||
}}
|
}}
|
||||||
/>
|
handleObjectClick={(data, options) => {
|
||||||
|
if (data.database) {
|
||||||
|
if (options.focusTab) {
|
||||||
|
switchCurrentDatabase({ connection: data.connection, name: data.database });
|
||||||
|
// console.log('FOCUSING DB', passProps);
|
||||||
|
// passProps?.onFocusSqlObjectList?.();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (options.focusTab) {
|
||||||
|
openConnection(data.connection);
|
||||||
|
} else {
|
||||||
|
openNewTab({
|
||||||
|
title: getConnectionLabel(data.connection),
|
||||||
|
icon: 'img connection',
|
||||||
|
tabComponent: 'ConnectionTab',
|
||||||
|
tabPreviewMode: options.tabPreviewMode,
|
||||||
|
props: {
|
||||||
|
conid: data.conid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppObjectList
|
||||||
|
list={connectionsWithParent}
|
||||||
|
module={connectionAppObject}
|
||||||
|
subItemsComponent={SubDatabaseList}
|
||||||
|
expandOnClick
|
||||||
|
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||||
|
{filter}
|
||||||
|
passProps={{ ...passProps, connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||||
|
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||||
|
setIsExpanded={(data, value) => {
|
||||||
|
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||||
|
}}
|
||||||
|
groupIconFunc={chevronExpandIcon}
|
||||||
|
groupFunc={data => data.parent}
|
||||||
|
expandIconFunc={plusExpandIcon}
|
||||||
|
onDropOnGroup={handleDropOnGroup}
|
||||||
|
emptyGroupNames={$emptyConnectionGroupNames}
|
||||||
|
sortGroups
|
||||||
|
groupContextMenu={createGroupContextMenu}
|
||||||
|
collapsedGroupNames={collapsedConnectionGroupNames}
|
||||||
|
/>
|
||||||
|
{#if (connectionsWithParent?.length > 0 && connectionsWithoutParent?.length > 0) || ($emptyConnectionGroupNames.length > 0 && connectionsWithoutParent?.length > 0)}
|
||||||
|
<div class="br" />
|
||||||
|
{/if}
|
||||||
|
<AppObjectList
|
||||||
|
list={connectionsWithoutParent}
|
||||||
|
module={connectionAppObject}
|
||||||
|
subItemsComponent={SubDatabaseList}
|
||||||
|
expandOnClick
|
||||||
|
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||||
|
{filter}
|
||||||
|
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||||
|
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||||
|
setIsExpanded={(data, value) => {
|
||||||
|
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</AppObjectListHandler>
|
||||||
{#if $connections && !$connections.find(x => !x.unsaved) && $openedConnections.length == 0 && $commandsCustomized['new.connection']?.enabled && !$openedTabs.find(x => !x.closedTime && x.tabComponent == 'ConnectionTab' && !x.props?.conid)}
|
{#if $connections && !$connections.find(x => !x.unsaved) && $openedConnections.length == 0 && $commandsCustomized['new.connection']?.enabled && !$openedTabs.find(x => !x.closedTime && x.tabComponent == 'ConnectionTab' && !x.props?.conid)}
|
||||||
<LargeButton icon="icon new-connection" on:click={() => runCommand('new.connection')} fillHorizontal
|
<LargeButton icon="icon new-connection" on:click={() => runCommand('new.connection')} fillHorizontal
|
||||||
>Add new connection</LargeButton
|
>Add new connection</LargeButton
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
export let hidden = false;
|
export let hidden = false;
|
||||||
|
let domSqlObjectList = null;
|
||||||
|
|
||||||
$: conid = $currentDatabase?.connection?._id;
|
$: conid = $currentDatabase?.connection?._id;
|
||||||
$: connection = useConnectionInfo({ conid });
|
$: connection = useConnectionInfo({ conid });
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
{:else if !$config?.singleDbConnection}
|
{:else if !$config?.singleDbConnection}
|
||||||
<WidgetColumnBarItem title="Connections" name="connections" height="35%" storageName="connectionsWidget">
|
<WidgetColumnBarItem title="Connections" name="connections" height="35%" storageName="connectionsWidget">
|
||||||
<ConnectionList />
|
<ConnectionList passProps={{ onFocusSqlObjectList: () => domSqlObjectList.focus() }} />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
{/if}
|
{/if}
|
||||||
<WidgetColumnBarItem
|
<WidgetColumnBarItem
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
|
|
||||||
<WidgetColumnBarItem
|
<WidgetColumnBarItem
|
||||||
title={driver?.databaseEngineTypes?.includes('document')
|
title={driver?.databaseEngineTypes?.includes('document')
|
||||||
? driver?.collectionPluralLabel ?? 'Collections/containers'
|
? (driver?.collectionPluralLabel ?? 'Collections/containers')
|
||||||
: 'Tables, views, functions'}
|
: 'Tables, views, functions'}
|
||||||
name="dbObjects"
|
name="dbObjects"
|
||||||
storageName="dbObjectsWidget"
|
storageName="dbObjectsWidget"
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
(driver?.databaseEngineTypes?.includes('sql') || driver?.databaseEngineTypes?.includes('document'))
|
(driver?.databaseEngineTypes?.includes('sql') || driver?.databaseEngineTypes?.includes('document'))
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<SqlObjectList {conid} {database} />
|
<SqlObjectList {conid} {database} bind:this={domSqlObjectList} />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
|
|
||||||
<WidgetColumnBarItem
|
<WidgetColumnBarItem
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
import { chevronExpandIcon } from '../icons/expandIcons';
|
import { chevronExpandIcon } from '../icons/expandIcons';
|
||||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
import { getObjectTypeFieldLabel } from '../utility/common';
|
import { getObjectTypeFieldLabel, switchCurrentDatabase } from '../utility/common';
|
||||||
import DropDownButton from '../buttons/DropDownButton.svelte';
|
import DropDownButton from '../buttons/DropDownButton.svelte';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
extensions,
|
extensions,
|
||||||
|
focusedConnectionOrDatabase,
|
||||||
getSelectedDatabaseObjectAppObject,
|
getSelectedDatabaseObjectAppObject,
|
||||||
selectedDatabaseObjectAppObject,
|
selectedDatabaseObjectAppObject,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
@@ -50,6 +51,8 @@
|
|||||||
import { appliedCurrentSchema } from '../stores';
|
import { appliedCurrentSchema } from '../stores';
|
||||||
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
||||||
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
||||||
|
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||||
|
import clickOutside from '../utility/clickOutside';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
@@ -125,6 +128,15 @@
|
|||||||
if (matcher && !matcher(filter)) return false;
|
if (matcher && !matcher(filter)) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function focus() {
|
||||||
|
domListHandler?.focusFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
$: differentFocusedDb =
|
||||||
|
$focusedConnectionOrDatabase &&
|
||||||
|
$focusedConnectionOrDatabase?.database &&
|
||||||
|
($focusedConnectionOrDatabase.conid != conid || $focusedConnectionOrDatabase?.database != database);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $status && $status.name == 'error'}
|
{#if $status && $status.name == 'error'}
|
||||||
@@ -185,7 +197,36 @@
|
|||||||
negativeMarginTop
|
negativeMarginTop
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WidgetsInnerContainer bind:this={domContainer}>
|
{#if differentFocusedDb}
|
||||||
|
<div class="no-focused-info">
|
||||||
|
<div class="m-1">Current database:</div>
|
||||||
|
<div class="m-1 ml-3 mb-3">
|
||||||
|
<b>{database}</b>
|
||||||
|
</div>
|
||||||
|
<FormStyledButton
|
||||||
|
value={`Switch to ${$focusedConnectionOrDatabase?.database}`}
|
||||||
|
skipWidth
|
||||||
|
on:click={() =>
|
||||||
|
switchCurrentDatabase({
|
||||||
|
connection: $focusedConnectionOrDatabase?.connection,
|
||||||
|
name: $focusedConnectionOrDatabase?.database,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<FormStyledButton
|
||||||
|
value={`Show ${database}`}
|
||||||
|
skipWidth
|
||||||
|
on:click={() => {
|
||||||
|
$focusedConnectionOrDatabase = {
|
||||||
|
conid,
|
||||||
|
database,
|
||||||
|
connection: $connection,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<WidgetsInnerContainer bind:this={domContainer} hideContent={differentFocusedDb}>
|
||||||
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
||||||
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
||||||
{:else}
|
{:else}
|
||||||
@@ -195,12 +236,12 @@
|
|||||||
selectedObjectStore={selectedDatabaseObjectAppObject}
|
selectedObjectStore={selectedDatabaseObjectAppObject}
|
||||||
getSelectedObject={getSelectedDatabaseObjectAppObject}
|
getSelectedObject={getSelectedDatabaseObjectAppObject}
|
||||||
selectedObjectMatcher={matchDatabaseObjectAppObject}
|
selectedObjectMatcher={matchDatabaseObjectAppObject}
|
||||||
module={databaseObjectAppObject}
|
handleObjectClick={(data, options) => databaseObjectAppObject.handleObjectClick(data, options)}
|
||||||
onScrollTop={() => {
|
onScrollTop={() => {
|
||||||
domContainer?.scrollTop();
|
domContainer?.scrollTop();
|
||||||
}}
|
}}
|
||||||
onFocusFilterBox={() => {
|
onFocusFilterBox={text => {
|
||||||
domFilter?.focus();
|
domFilter?.focus(text);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
@@ -224,3 +265,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</WidgetsInnerContainer>
|
</WidgetsInnerContainer>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.no-focused-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
let domDiv;
|
let domDiv;
|
||||||
|
|
||||||
|
export let hideContent = false;
|
||||||
|
|
||||||
export function scrollTop() {
|
export function scrollTop() {
|
||||||
domDiv.scrollTop = 0;
|
domDiv.scrollTop = 0;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div on:drop bind:this={domDiv}><slot /></div>
|
<div on:drop bind:this={domDiv} class:hideContent><slot /></div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
@@ -15,4 +17,8 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
width: var(--dim-left-panel-width);
|
width: var(--dim-left-panel-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hideContent {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user