mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 14:06:00 +00:00
Merge branch 'feature/tab-preview-mode'
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
export let module = null;
|
||||
|
||||
export let isBold = false;
|
||||
export let isChoosed = false;
|
||||
export let isBusy = false;
|
||||
export let statusIcon = undefined;
|
||||
export let statusIconBefore = undefined;
|
||||
@@ -66,11 +67,21 @@
|
||||
}
|
||||
|
||||
// $: console.log(title, indentLevel);
|
||||
let domDiv;
|
||||
|
||||
$: if (isBold && domDiv) {
|
||||
domDiv.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
||||
}
|
||||
|
||||
$: if (isChoosed && domDiv) {
|
||||
domDiv.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="main"
|
||||
class:isBold
|
||||
class:isChoosed
|
||||
draggable={true}
|
||||
on:click={handleClick}
|
||||
on:mouseup={handleMouseUp}
|
||||
@@ -83,6 +94,7 @@
|
||||
on:dragenter
|
||||
on:dragend
|
||||
on:drop
|
||||
bind:this={domDiv}
|
||||
>
|
||||
{#if checkedObjectsStore}
|
||||
<CheckboxField
|
||||
@@ -180,6 +192,12 @@
|
||||
.isBold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.isChoosed {
|
||||
background-color: var(--theme-bg-3);
|
||||
}
|
||||
:global(.app-object-list-focused) .isChoosed {
|
||||
background-color: var(--theme-bg-selected);
|
||||
}
|
||||
.status {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
export let emptyGroupNames = [];
|
||||
|
||||
export let collapsedGroupNames = writable([]);
|
||||
export let onChangeFilteredList;
|
||||
|
||||
$: filtered = !groupFunc
|
||||
? list.filter(data => {
|
||||
|
||||
@@ -509,9 +509,9 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
||||
extInfo={data.extInfo}
|
||||
icon="img database"
|
||||
colorMark={passProps?.connectionColorFactory &&
|
||||
passProps?.connectionColorFactory({ conid: _.get(data.connection, '_id'), database: data.name }, null, null, false)}
|
||||
isBold={_.get($currentDatabase, 'connection._id') == _.get(data.connection, '_id') &&
|
||||
extractDbNameFromComposite(_.get($currentDatabase, 'name')) == data.name}
|
||||
passProps?.connectionColorFactory({ conid: data?.connection?._id, database: data.name }, null, null, false)}
|
||||
isBold={$currentDatabase?.connection?._id == data?.connection?._id &&
|
||||
extractDbNameFromComposite($currentDatabase?.name) == data.name}
|
||||
on:click={() => switchCurrentDatabase(data)}
|
||||
on:dragstart
|
||||
on:dragenter
|
||||
|
||||
@@ -29,12 +29,18 @@
|
||||
views: 'ViewDataTab',
|
||||
matviews: 'ViewDataTab',
|
||||
queries: 'QueryDataTab',
|
||||
procedures: 'SqlObjectTab',
|
||||
functions: 'SqlObjectTab',
|
||||
};
|
||||
|
||||
function createMenusCore(
|
||||
objectTypeField,
|
||||
driver
|
||||
): {
|
||||
function createScriptTemplatesSubmenu(objectTypeField) {
|
||||
return {
|
||||
label: 'SQL template',
|
||||
submenu: getSupportedScriptTemplates(objectTypeField),
|
||||
};
|
||||
}
|
||||
|
||||
interface DbObjMenuItem {
|
||||
label?: string;
|
||||
tab?: string;
|
||||
forceNewTab?: boolean;
|
||||
@@ -53,34 +59,22 @@
|
||||
isExport?: boolean;
|
||||
isImport?: boolean;
|
||||
isActiveChart?: boolean;
|
||||
isShowSql?: boolean;
|
||||
scriptTemplate?: string;
|
||||
sqlGeneratorProps?: any;
|
||||
isDropCollection?: boolean;
|
||||
isRenameCollection?: boolean;
|
||||
isDuplicateCollection?: boolean;
|
||||
}[] {
|
||||
submenu?: DbObjMenuItem[];
|
||||
}
|
||||
|
||||
function createMenusCore(objectTypeField, driver): DbObjMenuItem[] {
|
||||
switch (objectTypeField) {
|
||||
case 'tables':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['tables'],
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: 'TableDataTab',
|
||||
forceNewTab: true,
|
||||
},
|
||||
{
|
||||
label: 'Open form',
|
||||
tab: 'TableDataTab',
|
||||
forceNewTab: true,
|
||||
initialData: {
|
||||
grid: {
|
||||
isFormView: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
icon: 'img table-structure',
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Design query',
|
||||
@@ -93,6 +87,33 @@
|
||||
forceNewTab: true,
|
||||
icon: 'img perspective',
|
||||
},
|
||||
createScriptTemplatesSubmenu('tables'),
|
||||
{
|
||||
label: 'SQL generator',
|
||||
submenu: [
|
||||
{
|
||||
label: 'CREATE TABLE',
|
||||
sqlGeneratorProps: {
|
||||
createTables: true,
|
||||
createIndexes: true,
|
||||
createForeignKeys: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DROP TABLE',
|
||||
sqlGeneratorProps: {
|
||||
dropTables: true,
|
||||
dropReferences: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'INSERT',
|
||||
sqlGeneratorProps: {
|
||||
insert: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
@@ -142,50 +163,12 @@
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE TABLE',
|
||||
scriptTemplate: 'CREATE TABLE',
|
||||
},
|
||||
{
|
||||
label: 'SQL: SELECT',
|
||||
scriptTemplate: 'SELECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: CREATE TABLE',
|
||||
sqlGeneratorProps: {
|
||||
createTables: true,
|
||||
createIndexes: true,
|
||||
createForeignKeys: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: DROP TABLE',
|
||||
sqlGeneratorProps: {
|
||||
dropTables: true,
|
||||
dropReferences: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: INSERT',
|
||||
sqlGeneratorProps: {
|
||||
insert: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
case 'views':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['views'],
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: 'ViewDataTab',
|
||||
forceNewTab: true,
|
||||
},
|
||||
{
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
icon: 'img view-structure',
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Design query',
|
||||
@@ -197,6 +180,27 @@
|
||||
forceNewTab: true,
|
||||
icon: 'img perspective',
|
||||
},
|
||||
createScriptTemplatesSubmenu('views'),
|
||||
{
|
||||
label: 'SQL generator',
|
||||
submenu: [
|
||||
{
|
||||
label: 'CREATE VIEW',
|
||||
sqlGeneratorProps: {
|
||||
createViews: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DROP VIEW',
|
||||
sqlGeneratorProps: {
|
||||
dropViews: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
hasPermission('dbops/model/edit') && {
|
||||
label: 'Drop view',
|
||||
isDrop: true,
|
||||
@@ -219,48 +223,12 @@
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE VIEW',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: ALTER VIEW',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE TABLE',
|
||||
scriptTemplate: 'CREATE TABLE',
|
||||
},
|
||||
{
|
||||
label: 'SQL: SELECT',
|
||||
scriptTemplate: 'SELECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: CREATE VIEW',
|
||||
sqlGeneratorProps: {
|
||||
createViews: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: DROP VIEW',
|
||||
sqlGeneratorProps: {
|
||||
dropViews: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
case 'matviews':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['matviews'],
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: 'ViewDataTab',
|
||||
forceNewTab: true,
|
||||
},
|
||||
{
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
divider: true,
|
||||
},
|
||||
hasPermission('dbops/model/edit') && {
|
||||
label: 'Drop view',
|
||||
@@ -272,10 +240,31 @@
|
||||
isRename: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Query designer',
|
||||
isQueryDesigner: true,
|
||||
},
|
||||
createScriptTemplatesSubmenu('matviews'),
|
||||
{
|
||||
label: 'SQL generator',
|
||||
submenu: [
|
||||
{
|
||||
label: 'CREATE MATERIALIZED VIEW',
|
||||
sqlGeneratorProps: {
|
||||
createMatviews: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DROP MATERIALIZED VIEW',
|
||||
sqlGeneratorProps: {
|
||||
dropMatviews: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
@@ -288,37 +277,6 @@
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE MATERIALIZED VIEW',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: ALTER MATERIALIZED VIEW',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE TABLE',
|
||||
scriptTemplate: 'CREATE TABLE',
|
||||
},
|
||||
{
|
||||
label: 'SQL: SELECT',
|
||||
scriptTemplate: 'SELECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: CREATE MATERIALIZED VIEW',
|
||||
sqlGeneratorProps: {
|
||||
createMatviews: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: DROP MATERIALIZED VIEW',
|
||||
sqlGeneratorProps: {
|
||||
dropMatviews: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
case 'queries':
|
||||
return [
|
||||
@@ -330,6 +288,10 @@
|
||||
];
|
||||
case 'procedures':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['procedures'],
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
hasPermission('dbops/model/edit') && {
|
||||
label: 'Drop procedure',
|
||||
isDrop: true,
|
||||
@@ -340,33 +302,31 @@
|
||||
isRename: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
createScriptTemplatesSubmenu('procedures'),
|
||||
{
|
||||
label: 'SQL: CREATE PROCEDURE',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: ALTER PROCEDURE',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: EXECUTE',
|
||||
scriptTemplate: 'EXECUTE PROCEDURE',
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: CREATE PROCEDURE',
|
||||
sqlGeneratorProps: {
|
||||
createProcedures: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: DROP PROCEDURE',
|
||||
sqlGeneratorProps: {
|
||||
dropProcedures: true,
|
||||
},
|
||||
label: 'SQL generator',
|
||||
submenu: [
|
||||
{
|
||||
label: 'CREATE PROCEDURE',
|
||||
sqlGeneratorProps: {
|
||||
createProcedures: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DROP PROCEDURE',
|
||||
sqlGeneratorProps: {
|
||||
dropProcedures: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
case 'functions':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['functions'],
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
hasPermission('dbops/model/edit') && {
|
||||
label: 'Drop function',
|
||||
isDrop: true,
|
||||
@@ -377,43 +337,30 @@
|
||||
isRename: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
createScriptTemplatesSubmenu('functions'),
|
||||
{
|
||||
label: 'SQL: CREATE FUNCTION',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL: ALTER FUNCTION',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: CREATE FUNCTION',
|
||||
sqlGeneratorProps: {
|
||||
createFunctions: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL Generator: DROP FUNCTION',
|
||||
sqlGeneratorProps: {
|
||||
dropFunctions: true,
|
||||
},
|
||||
label: 'SQL generator',
|
||||
submenu: [
|
||||
{
|
||||
label: 'CREATE FUNCTION',
|
||||
sqlGeneratorProps: {
|
||||
createFunctions: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DROP FUNCTION',
|
||||
sqlGeneratorProps: {
|
||||
dropFunctions: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
case 'collections':
|
||||
return [
|
||||
...defaultDatabaseObjectAppObjectActions['collections'],
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: 'CollectionDataTab',
|
||||
forceNewTab: true,
|
||||
},
|
||||
{
|
||||
label: 'Open JSON',
|
||||
tab: 'CollectionDataTab',
|
||||
forceNewTab: true,
|
||||
initialData: {
|
||||
grid: {
|
||||
isJsonView: true,
|
||||
},
|
||||
},
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Design perspective query',
|
||||
@@ -659,15 +606,30 @@
|
||||
// fixedTargetPureName: data.pureName,
|
||||
// },
|
||||
// });
|
||||
// } else if (menu.isShowSql) {
|
||||
// openNewTab({
|
||||
// title: data.pureName,
|
||||
// icon: 'img sql-file',
|
||||
// tabComponent: 'SqlObjectTab',
|
||||
// tabPreviewMode: true,
|
||||
// props: {
|
||||
// conid: data.conid,
|
||||
// database: data.database,
|
||||
// schemaName: data.schemaName,
|
||||
// pureName: data.pureName,
|
||||
// objectTypeField: data.objectTypeField,
|
||||
// },
|
||||
// });
|
||||
} else {
|
||||
openDatabaseObjectDetail(
|
||||
menu.tab,
|
||||
menu.scriptTemplate,
|
||||
data,
|
||||
{ ...data, defaultActionId: menu.defaultActionId },
|
||||
menu.forceNewTab,
|
||||
menu.initialData,
|
||||
menu.icon,
|
||||
data
|
||||
data,
|
||||
!!menu.defaultActionId
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -697,11 +659,12 @@
|
||||
export async function openDatabaseObjectDetail(
|
||||
tabComponent,
|
||||
scriptTemplate,
|
||||
{ schemaName, pureName, conid, database, objectTypeField },
|
||||
{ schemaName, pureName, conid, database, objectTypeField, defaultActionId },
|
||||
forceNewTab?,
|
||||
initialData?,
|
||||
icon?,
|
||||
appObjectData?
|
||||
appObjectData?,
|
||||
tabPreviewMode?
|
||||
) {
|
||||
const connection = await getConnectionInfo({ conid });
|
||||
const tooltip = `${getConnectionLabel(connection)}\n${database}\n${fullDisplayName({
|
||||
@@ -711,12 +674,16 @@
|
||||
|
||||
openNewTab(
|
||||
{
|
||||
title: scriptTemplate ? 'Query #' : getObjectTitle(connection, schemaName, pureName),
|
||||
// title: getObjectTitle(connection, schemaName, pureName),
|
||||
title: tabComponent ? getObjectTitle(connection, schemaName, pureName) : 'Query #',
|
||||
tooltip,
|
||||
icon: icon || (scriptTemplate ? 'img sql-file' : databaseObjectIcons[objectTypeField]),
|
||||
tabComponent: scriptTemplate ? 'QueryTab' : tabComponent,
|
||||
icon:
|
||||
icon ||
|
||||
(scriptTemplate || tabComponent == 'SqlObjectTab' ? 'img sql-file' : databaseObjectIcons[objectTypeField]),
|
||||
tabComponent: tabComponent ?? 'QueryTab',
|
||||
appObject: 'DatabaseObjectAppObject',
|
||||
appObjectData,
|
||||
tabPreviewMode,
|
||||
props: {
|
||||
schemaName,
|
||||
pureName,
|
||||
@@ -724,6 +691,7 @@
|
||||
database,
|
||||
objectTypeField,
|
||||
initialArgs: scriptTemplate ? { scriptTemplate } : null,
|
||||
defaultActionId,
|
||||
},
|
||||
},
|
||||
initialData,
|
||||
@@ -731,31 +699,62 @@
|
||||
);
|
||||
}
|
||||
|
||||
export function handleDatabaseObjectClick(data, forceNewTab = false) {
|
||||
export function handleDatabaseObjectClick(
|
||||
data,
|
||||
{ forceNewTab = false, tabPreviewMode = false, focusTab = false } = {}
|
||||
) {
|
||||
const { schemaName, pureName, conid, database, objectTypeField } = data;
|
||||
const driver = findEngineDriver(data, getExtensions());
|
||||
|
||||
const configuredAction = getCurrentSettings()[`defaultAction.dbObjectClick.${objectTypeField}`];
|
||||
const overrideMenu = createMenus(objectTypeField, driver).find(x => x.label && x.label == configuredAction);
|
||||
if (overrideMenu) {
|
||||
databaseObjectMenuClickHandler(data, overrideMenu);
|
||||
const activeTab = getActiveTab();
|
||||
const activeTabProps = activeTab?.props || {};
|
||||
const activeDefaultActionId = activeTab?.props?.defaultActionId;
|
||||
|
||||
if (matchDatabaseObjectAppObject(data, activeTabProps)) {
|
||||
if (!tabPreviewMode) {
|
||||
openedTabs.update(tabs => {
|
||||
return tabs.map(tab => ({
|
||||
...tab,
|
||||
tabPreviewMode: tab.tabid == activeTab.tabid ? false : tab.tabPreviewMode,
|
||||
focused: focusTab && tab.tabid == activeTab.tabid ? true : tab.focused,
|
||||
}));
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField];
|
||||
|
||||
const configuredActionId = getCurrentSettings()[`defaultAction.dbObjectClick.${objectTypeField}`];
|
||||
const prefferedAction =
|
||||
availableDefaultActions.find(x => x.defaultActionId == activeDefaultActionId) ??
|
||||
availableDefaultActions.find(x => x.defaultActionId == configuredActionId) ??
|
||||
availableDefaultActions[0];
|
||||
|
||||
// console.log('activeTab', activeTab);
|
||||
|
||||
// const overrideMenu = createMenus(objectTypeField, driver).find(x => x.label && x.label == configuredAction);
|
||||
// if (overrideMenu) {
|
||||
// databaseObjectMenuClickHandler(data, overrideMenu);
|
||||
// return;
|
||||
// }
|
||||
|
||||
openDatabaseObjectDetail(
|
||||
defaultTabs[objectTypeField],
|
||||
defaultTabs[objectTypeField] ? null : 'CREATE OBJECT',
|
||||
prefferedAction.tab,
|
||||
activeTabProps?.scriptTemplate,
|
||||
{
|
||||
schemaName,
|
||||
pureName,
|
||||
conid,
|
||||
database,
|
||||
objectTypeField,
|
||||
defaultActionId: prefferedAction.defaultActionId,
|
||||
},
|
||||
forceNewTab,
|
||||
null,
|
||||
null,
|
||||
data
|
||||
data,
|
||||
tabPreviewMode
|
||||
);
|
||||
}
|
||||
|
||||
@@ -769,64 +768,74 @@
|
||||
);
|
||||
}
|
||||
|
||||
function menuItemMapper(menu, data, connection) {
|
||||
if (menu.divider) return menu;
|
||||
|
||||
if (menu.isExport) {
|
||||
return createQuickExportMenu(
|
||||
fmt => async () => {
|
||||
const coninfo = await getConnectionInfo(data);
|
||||
exportQuickExportFile(
|
||||
data.pureName,
|
||||
{
|
||||
functionName: menu.functionName,
|
||||
props: {
|
||||
connection: extractShellConnection(coninfo, data.database),
|
||||
..._.pick(data, ['pureName', 'schemaName']),
|
||||
},
|
||||
},
|
||||
fmt
|
||||
);
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
openImportExportTab({
|
||||
sourceStorageType: 'database',
|
||||
sourceConnectionId: data.conid,
|
||||
sourceDatabaseName: extractDbNameFromComposite(data.database),
|
||||
sourceSchemaName: data.schemaName,
|
||||
sourceList: [data.pureName],
|
||||
});
|
||||
// showModal(ImportExportModal, {
|
||||
// initialValues: {
|
||||
// sourceStorageType: 'database',
|
||||
// sourceConnectionId: data.conid,
|
||||
// sourceDatabaseName: data.database,
|
||||
// sourceSchemaName: data.schemaName,
|
||||
// sourceList: [data.pureName],
|
||||
// },
|
||||
// });
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (connection?.isReadOnly && menu.requiresWriteAccess) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (menu.submenu) {
|
||||
return {
|
||||
...menu,
|
||||
submenu: menu.submenu.map(x => menuItemMapper(x, data, connection)),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
text: menu.label,
|
||||
onClick: () => {
|
||||
databaseObjectMenuClickHandler(data, menu);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createDatabaseObjectMenu(data, connection = null) {
|
||||
const driver = findEngineDriver(data, getExtensions());
|
||||
|
||||
const { objectTypeField } = data;
|
||||
return createMenus(objectTypeField, driver)
|
||||
.filter(x => x)
|
||||
.map(menu => {
|
||||
if (menu.divider) return menu;
|
||||
|
||||
if (menu.isExport) {
|
||||
return createQuickExportMenu(
|
||||
fmt => async () => {
|
||||
const coninfo = await getConnectionInfo(data);
|
||||
exportQuickExportFile(
|
||||
data.pureName,
|
||||
{
|
||||
functionName: menu.functionName,
|
||||
props: {
|
||||
connection: extractShellConnection(coninfo, data.database),
|
||||
..._.pick(data, ['pureName', 'schemaName']),
|
||||
},
|
||||
},
|
||||
fmt
|
||||
);
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
openImportExportTab({
|
||||
sourceStorageType: 'database',
|
||||
sourceConnectionId: data.conid,
|
||||
sourceDatabaseName: extractDbNameFromComposite(data.database),
|
||||
sourceSchemaName: data.schemaName,
|
||||
sourceList: [data.pureName],
|
||||
});
|
||||
// showModal(ImportExportModal, {
|
||||
// initialValues: {
|
||||
// sourceStorageType: 'database',
|
||||
// sourceConnectionId: data.conid,
|
||||
// sourceDatabaseName: data.database,
|
||||
// sourceSchemaName: data.schemaName,
|
||||
// sourceList: [data.pureName],
|
||||
// },
|
||||
// });
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (connection?.isReadOnly && menu.requiresWriteAccess) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
text: menu.label,
|
||||
onClick: () => {
|
||||
databaseObjectMenuClickHandler(data, menu);
|
||||
},
|
||||
};
|
||||
});
|
||||
.map(menu => menuItemMapper(menu, data, connection));
|
||||
}
|
||||
|
||||
function formatRowCount(value) {
|
||||
@@ -838,6 +847,10 @@
|
||||
export function createAppObjectMenu(data) {
|
||||
return createDatabaseObjectMenu(data);
|
||||
}
|
||||
|
||||
export function handleObjectClick(data, { forceNewTab = false, tabPreviewMode = false, focusTab = false }) {
|
||||
return handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -846,10 +859,13 @@
|
||||
import {
|
||||
currentDatabase,
|
||||
extensions,
|
||||
getActiveTab,
|
||||
getCurrentSettings,
|
||||
getExtensions,
|
||||
openedConnections,
|
||||
openedTabs,
|
||||
pinnedTables,
|
||||
selectedDatabaseObjectAppObject,
|
||||
} from '../stores';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import {
|
||||
@@ -877,12 +893,15 @@
|
||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import { openImportExportTab } from '../utility/importExportTools';
|
||||
import { defaultDatabaseObjectAppObjectActions, matchDatabaseObjectAppObject } from './appObjectTools';
|
||||
import { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
|
||||
|
||||
export let data;
|
||||
export let passProps;
|
||||
|
||||
function handleClick(forceNewTab = false) {
|
||||
handleDatabaseObjectClick(data, forceNewTab);
|
||||
function handleClick({ forceNewTab = false, tabPreviewMode = false, focusTab = false } = {}) {
|
||||
$selectedDatabaseObjectAppObject = _.pick(data, ['conid', 'database', 'objectTypeField', 'pureName', 'schemaName']);
|
||||
handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
|
||||
}
|
||||
|
||||
function createMenu() {
|
||||
@@ -915,8 +934,10 @@
|
||||
onPin={isPinned ? null : () => pinnedTables.update(list => [...list, data])}
|
||||
onUnpin={isPinned ? () => pinnedTables.update(list => list.filter(x => !testEqual(x, data))) : null}
|
||||
extInfo={getExtInfo(data)}
|
||||
on:click={() => handleClick()}
|
||||
on:middleclick={() => handleClick(true)}
|
||||
isChoosed={matchDatabaseObjectAppObject($selectedDatabaseObjectAppObject, data)}
|
||||
on:click={() => handleClick({ tabPreviewMode: true })}
|
||||
on:middleclick={() => handleClick({ forceNewTab: true })}
|
||||
on:dblclick={() => handleClick({ tabPreviewMode: false, focusTab: true })}
|
||||
on:expand
|
||||
on:dragstart
|
||||
on:dragenter
|
||||
|
||||
80
packages/web/src/appobj/appObjectTools.ts
Normal file
80
packages/web/src/appobj/appObjectTools.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
export function matchDatabaseObjectAppObject(obj1, obj2) {
|
||||
return (
|
||||
obj1?.objectTypeField == obj2?.objectTypeField &&
|
||||
obj1?.conid == obj2?.conid &&
|
||||
obj1?.database == obj2?.database &&
|
||||
obj1?.pureName == obj2?.pureName &&
|
||||
obj1?.schemaName == obj2?.schemaName
|
||||
);
|
||||
}
|
||||
|
||||
function getTableLikeActions(dataTab) {
|
||||
return [
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: dataTab,
|
||||
defaultActionId: 'openTable',
|
||||
},
|
||||
{
|
||||
label: 'Open form',
|
||||
tab: dataTab,
|
||||
initialData: {
|
||||
grid: {
|
||||
isFormView: true,
|
||||
},
|
||||
},
|
||||
defaultActionId: 'openForm',
|
||||
},
|
||||
{
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
icon: 'img table-structure',
|
||||
defaultActionId: 'openStructure',
|
||||
},
|
||||
{
|
||||
label: 'Show SQL',
|
||||
tab: 'SqlObjectTab',
|
||||
defaultActionId: 'showSql',
|
||||
icon: 'img sql-file',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export const defaultDatabaseObjectAppObjectActions = {
|
||||
tables: getTableLikeActions('TableDataTab'),
|
||||
views: getTableLikeActions('ViewDataTab'),
|
||||
matviews: getTableLikeActions('ViewDataTab'),
|
||||
procedures: [
|
||||
{
|
||||
label: 'Show SQL',
|
||||
tab: 'SqlObjectTab',
|
||||
defaultActionId: 'showSql',
|
||||
icon: 'img sql-file',
|
||||
},
|
||||
],
|
||||
functions: [
|
||||
{
|
||||
label: 'Show SQL',
|
||||
tab: 'SqlObjectTab',
|
||||
defaultActionId: 'showSql',
|
||||
icon: 'img sql-file',
|
||||
},
|
||||
],
|
||||
collections: [
|
||||
{
|
||||
label: 'Open data',
|
||||
tab: 'CollectionDataTab',
|
||||
defaultActionId: 'openTable',
|
||||
},
|
||||
{
|
||||
label: 'Open JSON',
|
||||
tab: 'CollectionDataTab',
|
||||
defaultActionId: 'openJson',
|
||||
initialData: {
|
||||
grid: {
|
||||
isJsonView: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -142,6 +142,8 @@
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let isColumnManagerFocused = false;
|
||||
</script>
|
||||
|
||||
{#if allowChangeChangeSetStructure}
|
||||
@@ -207,9 +209,13 @@
|
||||
bind:this={domFocusField}
|
||||
on:keydown={handleKeyDown}
|
||||
on:focus={() => {
|
||||
isColumnManagerFocused = true;
|
||||
// activator.activate();
|
||||
// invalidateCommands();
|
||||
}}
|
||||
on:blur={() => {
|
||||
isColumnManagerFocused = false;
|
||||
}}
|
||||
on:copy={copyToClipboard}
|
||||
/>
|
||||
|
||||
@@ -224,6 +230,7 @@
|
||||
{database}
|
||||
{tableInfo}
|
||||
{setTableInfo}
|
||||
{isColumnManagerFocused}
|
||||
columnInfo={tableInfo?.columns?.[columnIndex]}
|
||||
{columnIndex}
|
||||
{allowChangeChangeSetStructure}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
export let columnInfo = null;
|
||||
export let columnIndex = -1;
|
||||
|
||||
export let isColumnManagerFocused = false;
|
||||
|
||||
export let allowChangeChangeSetStructure = false;
|
||||
$: addDataCommand = allowChangeChangeSetStructure;
|
||||
|
||||
@@ -49,6 +51,7 @@
|
||||
else display.focusColumns([column.uniqueName]);
|
||||
}}
|
||||
class:isSelected
|
||||
class:isFocused={isColumnManagerFocused}
|
||||
on:click
|
||||
on:mousedown
|
||||
on:mousemove
|
||||
@@ -123,6 +126,10 @@
|
||||
}
|
||||
|
||||
.row.isSelected {
|
||||
background: var(--theme-bg-3);
|
||||
}
|
||||
|
||||
.row.isSelected.isFocused {
|
||||
background: var(--theme-bg-selected);
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,9 @@
|
||||
background: var(--theme-bg-volcano);
|
||||
}
|
||||
td.isSelected {
|
||||
background: var(--theme-bg-3);
|
||||
}
|
||||
:global(.data-grid-focused) td.isSelected {
|
||||
background: var(--theme-bg-selected);
|
||||
}
|
||||
td.isDeleted {
|
||||
|
||||
@@ -472,7 +472,7 @@
|
||||
export let dataEditorTypesBehaviourOverride = null;
|
||||
|
||||
const wheelRowCount = 5;
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
let containerHeight = 0;
|
||||
let containerWidth = 0;
|
||||
@@ -492,6 +492,8 @@
|
||||
let autofillSelectedCells = emptyCellArray;
|
||||
const domFilterControlsRef = createRef({});
|
||||
|
||||
let isGridFocused=false;
|
||||
|
||||
const tabid = getContext('tabid');
|
||||
|
||||
let unsubscribeDbRefresh;
|
||||
@@ -1041,7 +1043,6 @@
|
||||
return !hideGridLeftColumn;
|
||||
}
|
||||
|
||||
|
||||
$: autofillMarkerCell =
|
||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
|
||||
? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
|
||||
@@ -1134,7 +1135,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: if ($tabVisible && domFocusField && focusOnVisible) {
|
||||
$: if ($tabFocused && domFocusField && focusOnVisible) {
|
||||
domFocusField.focus();
|
||||
}
|
||||
|
||||
@@ -1863,6 +1864,7 @@
|
||||
{:else}
|
||||
<div
|
||||
class="container"
|
||||
class:data-grid-focused={isGridFocused}
|
||||
bind:clientWidth={containerWidth}
|
||||
bind:clientHeight={containerHeight}
|
||||
use:contextMenu={buildMenu}
|
||||
@@ -1877,10 +1879,14 @@
|
||||
on:focus={() => {
|
||||
activator.activate();
|
||||
invalidateCommands();
|
||||
isGridFocused = true;
|
||||
}}
|
||||
on:blur
|
||||
on:paste={handlePaste}
|
||||
on:copy={copyToClipboard}
|
||||
on:blur={handleBlur}
|
||||
on:blur={() => {
|
||||
isGridFocused = false;
|
||||
}}
|
||||
/>
|
||||
<table
|
||||
class="table"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
$: searchValue = value || '';
|
||||
export let isDebounced = false;
|
||||
export let onFocusFilteredList = null;
|
||||
|
||||
let domInput;
|
||||
|
||||
@@ -14,9 +15,17 @@
|
||||
if (e.keyCode == keycodes.escape) {
|
||||
value = '';
|
||||
}
|
||||
if (e.keyCode == keycodes.downArrow || e.keyCode == keycodes.pageDown || e.keyCode == keycodes.enter) {
|
||||
onFocusFilteredList?.();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedSet = _.debounce(x => (value = x), 500);
|
||||
|
||||
export function focus() {
|
||||
domInput.focus();
|
||||
}
|
||||
</script>
|
||||
|
||||
<input
|
||||
|
||||
@@ -217,12 +217,12 @@
|
||||
$: rowHeight = $dataGridRowHeight;
|
||||
let currentCell = [0, 0];
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
const domCells = {};
|
||||
|
||||
let domFocusField;
|
||||
|
||||
$: if ($tabVisible && domFocusField && focusOnVisible) {
|
||||
$: if ($tabFocused && domFocusField && focusOnVisible) {
|
||||
domFocusField.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
|
||||
--theme-border: #555;
|
||||
|
||||
--theme-bg-hover: #112a45;
|
||||
--theme-bg-selected: #15395b; /* blue-3 */
|
||||
--theme-bg-hover: #555;
|
||||
--theme-bg-selected: #1d4d7e; /* blue-4 */
|
||||
--theme-bg-selected-point: #1765ad; /* blue-5 */
|
||||
|
||||
--theme-bg-statusbar-inv: #0050b3;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
--theme-border: #ccc;
|
||||
|
||||
--theme-bg-hover: #bae7ff;
|
||||
--theme-bg-hover: #ccc;
|
||||
--theme-bg-selected: #91d5ff; /* blue-3 */
|
||||
--theme-bg-selected-point: #40a9ff; /* blue-5 */
|
||||
|
||||
|
||||
18
packages/web/src/settings/FormDefaultActionField.svelte
Normal file
18
packages/web/src/settings/FormDefaultActionField.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script alng="ts">
|
||||
import { defaultDatabaseObjectAppObjectActions } from '../appobj/appObjectTools';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
|
||||
export let label;
|
||||
export let objectTypeField;
|
||||
</script>
|
||||
|
||||
<FormSelectField
|
||||
{label}
|
||||
name={`defaultAction.dbObjectClick.${objectTypeField}`}
|
||||
isNative
|
||||
defaultValue={defaultDatabaseObjectAppObjectActions[objectTypeField][0]?.defaultActionId}
|
||||
options={defaultDatabaseObjectAppObjectActions[objectTypeField].map(x => ({
|
||||
value: x.defaultActionId,
|
||||
label: x.label,
|
||||
}))}
|
||||
/>
|
||||
@@ -38,6 +38,7 @@
|
||||
import { useSettings } from '../utility/metadataLoaders';
|
||||
import { derived } from 'svelte/store';
|
||||
import { safeFormatDate } from 'dbgate-tools';
|
||||
import FormDefaultActionField from './FormDefaultActionField.svelte';
|
||||
|
||||
const electron = getElectron();
|
||||
let restartWarning = false;
|
||||
@@ -288,56 +289,11 @@ ORDER BY
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label="Table click"
|
||||
name="defaultAction.dbObjectClick.tables"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{ value: '', label: 'Open data, or open existing' },
|
||||
{ value: 'Open data', label: 'Open data (always new tab)' },
|
||||
{ value: 'Open form', label: 'Open form (always new tab)' },
|
||||
{ value: 'Open structure', label: 'Open structure' },
|
||||
{ value: 'SQL: CREATE TABLE', label: 'SQL: CREATE' },
|
||||
{ value: 'SQL: SELECT', label: 'SQL: SELECT' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label="View click"
|
||||
name="defaultAction.dbObjectClick.views"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{ value: '', label: 'Open data, or open existing' },
|
||||
{ value: 'Open data', label: 'Open data (always new tab)' },
|
||||
{ value: 'SQL: CREATE VIEW', label: 'SQL: CREATE' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label="Materialized view click"
|
||||
name="defaultAction.dbObjectClick.matviews"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{ value: '', label: 'Open data, or open existing' },
|
||||
{ value: 'Open data', label: 'Open data (always new tab)' },
|
||||
{ value: 'SQL: CREATE MATERIALIZED VIEW', label: 'SQL: CREATE' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label="Procedure click"
|
||||
name="defaultAction.dbObjectClick.procedures"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{ value: '', label: 'SQL: CREATE' },
|
||||
{ value: 'SQL: EXECUTE', label: 'SQL: EXECUTE' },
|
||||
// { value: 'SQL: CREATE PROCEDURE', label: 'SQL: CREATE' },
|
||||
]}
|
||||
/>
|
||||
<FormDefaultActionField label="Table click" objectTypeField="tables" />
|
||||
<FormDefaultActionField label="View click" objectTypeField="views" />
|
||||
<FormDefaultActionField label="Materialized view click" objectTypeField="matviews" />
|
||||
<FormDefaultActionField label="Procedure click" objectTypeField="procedures" />
|
||||
<FormDefaultActionField label="Function click" objectTypeField="functions" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="5">
|
||||
<div class="heading">Confirmations</div>
|
||||
|
||||
@@ -22,6 +22,8 @@ export interface TabDefinition {
|
||||
tabOrder?: number;
|
||||
multiTabIndex?: number;
|
||||
unsaved?: boolean;
|
||||
tabPreviewMode?: boolean;
|
||||
focused?: boolean;
|
||||
}
|
||||
|
||||
export function writableWithStorage<T>(defaultValue: T, storageName) {
|
||||
@@ -91,7 +93,7 @@ export const openedConnections = writable([]);
|
||||
export const temporaryOpenedConnections = writable([]);
|
||||
export const openedSingleDatabaseConnections = writable([]);
|
||||
export const expandedConnections = writable([]);
|
||||
export const currentDatabase = writable(null);
|
||||
export const currentDatabase = writableWithForage(null, 'currentDatabase');
|
||||
export const openedTabs = writableWithForage<TabDefinition[]>([], getOpenedTabsStorageName(), x => [...(x || [])]);
|
||||
export const copyRowsFormat = writableWithStorage('textWithoutHeaders', 'copyRowsFormat');
|
||||
export const extensions = writable<ExtensionsDirectory>(null);
|
||||
@@ -155,6 +157,8 @@ export const activeDbKeysStore = writableWithStorage({}, 'activeDbKeysStore');
|
||||
export const appliedCurrentSchema = writable<string>(null);
|
||||
export const loadingSchemaLists = writable({}); // dict [`${conid}::${database}`]: true
|
||||
|
||||
export const selectedDatabaseObjectAppObject = writable(null);
|
||||
|
||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||
);
|
||||
@@ -324,6 +328,12 @@ appliedCurrentSchema.subscribe(value => {
|
||||
});
|
||||
export const getAppliedCurrentSchema = () => appliedCurrentSchemaValue;
|
||||
|
||||
let selectedDatabaseObjectAppObjectValue = null;
|
||||
selectedDatabaseObjectAppObject.subscribe(value => {
|
||||
selectedDatabaseObjectAppObjectValue = value;
|
||||
});
|
||||
export const getSelectedDatabaseObjectAppObject = () => selectedDatabaseObjectAppObjectValue;
|
||||
|
||||
let openedModalsValue = [];
|
||||
openedModals.subscribe(value => {
|
||||
openedModalsValue = value;
|
||||
|
||||
@@ -4,17 +4,22 @@
|
||||
|
||||
export let tabid;
|
||||
export let tabVisible;
|
||||
export let tabFocused;
|
||||
export let tabComponent;
|
||||
|
||||
const tabVisibleStore = writable(tabVisible);
|
||||
setContext('tabid', tabid);
|
||||
setContext('tabVisible', tabVisibleStore);
|
||||
|
||||
const tabVisibleStore = writable(tabVisible);
|
||||
setContext('tabVisible', tabVisibleStore);
|
||||
$: tabVisibleStore.set(tabVisible);
|
||||
|
||||
const tabFocusedStore = writable(tabFocused);
|
||||
setContext('tabFocused', tabFocusedStore);
|
||||
$: tabFocusedStore.set(tabFocused);
|
||||
</script>
|
||||
|
||||
<div class:tabVisible>
|
||||
<svelte:component this={tabComponent} {...$$restProps} {tabid} {tabVisible} />
|
||||
<svelte:component this={tabComponent} {...$$restProps} {tabid} {tabVisible} {tabFocused} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
{tabid}
|
||||
unsaved={openedTabsByTabId[tabid]?.unsaved}
|
||||
tabVisible={tabid == shownTab?.tabid}
|
||||
tabFocused={tabid == shownTab?.tabid && shownTab?.focused}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
const hasAnyOtherTab = getOpenedTabs().filter(x => !x.closedTime).length >= 1;
|
||||
const hasAnyModalOpen = getOpenedModals().length > 0;
|
||||
|
||||
return hasAnyOtherTab && !hasConfirmModalOpen;
|
||||
return hasAnyOtherTab && !hasAnyModalOpen;
|
||||
},
|
||||
onClick: closeCurrentTab,
|
||||
});
|
||||
@@ -323,6 +323,7 @@
|
||||
import CloseTabModal from '../modals/CloseTabModal.svelte';
|
||||
import SwitchDatabaseModal from '../modals/SwitchDatabaseModal.svelte';
|
||||
import { getConnectionLabel } from 'dbgate-tools';
|
||||
import { handleAfterTabClick } from '../utility/changeCurrentDbByTab';
|
||||
|
||||
export let multiTabIndex;
|
||||
export let shownTab;
|
||||
@@ -362,6 +363,7 @@
|
||||
return;
|
||||
}
|
||||
setSelectedTab(tabid);
|
||||
handleAfterTabClick();
|
||||
};
|
||||
|
||||
const handleMouseDown = (e, tabid) => {
|
||||
@@ -377,6 +379,17 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handleDoubleClick = (e, tabid) => {
|
||||
e.preventDefault();
|
||||
|
||||
openedTabs.update(tabs =>
|
||||
tabs.map(x => ({
|
||||
...x,
|
||||
tabPreviewMode: x.tabid == tabid ? false : x.tabPreviewMode,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
const getContextMenu = tab => () => {
|
||||
const { tabid, props, tabComponent, appObject, appObjectData } = tab;
|
||||
|
||||
@@ -596,9 +609,11 @@
|
||||
class:selected={$draggingTab || $draggingDbGroup
|
||||
? tab.tabid == $draggingTabTarget?.tabid
|
||||
: tab.tabid == shownTab?.tabid}
|
||||
class:preview={!!tab.tabPreviewMode}
|
||||
on:click={e => handleTabClick(e, tab.tabid)}
|
||||
on:mousedown={e => handleMouseDown(e, tab.tabid)}
|
||||
on:mouseup={e => handleMouseUp(e, tab.tabid)}
|
||||
on:dblclick={e => handleDoubleClick(e, tab.tabid)}
|
||||
use:contextMenu={getContextMenu(tab)}
|
||||
draggable={true}
|
||||
on:dragstart={async e => {
|
||||
@@ -742,6 +757,9 @@
|
||||
.file-tab-item.selected {
|
||||
background-color: var(--theme-bg-0);
|
||||
}
|
||||
.file-tab-item.preview {
|
||||
font-style: italic;
|
||||
}
|
||||
.file-name {
|
||||
margin-left: 5px;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -47,13 +47,13 @@
|
||||
export let tabid;
|
||||
export let savedFile;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('FavoriteEditorTab', false);
|
||||
|
||||
let domEditor;
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
|
||||
export let tabid;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('JsonEditorTab', false);
|
||||
|
||||
let domEditor;
|
||||
let domToolStrip;
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -81,14 +81,14 @@
|
||||
|
||||
let jslid = null;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('JsonLinesEditorTab', false);
|
||||
|
||||
let domEditor;
|
||||
let domToolStrip;
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,13 +41,13 @@
|
||||
|
||||
export let tabid;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('MarkdownEditorTab', false);
|
||||
|
||||
let domEditor;
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
},
|
||||
];
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
const timerLabel = useTimerLabel();
|
||||
|
||||
let busy = false;
|
||||
@@ -182,7 +182,7 @@
|
||||
invalidateCommands();
|
||||
}
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<AceEditor
|
||||
mode={driver?.editorMode || 'text'}
|
||||
mode={driver?.editorMode || 'sql'}
|
||||
value={$editorState.value || ''}
|
||||
splitterOptions={driver?.getQuerySplitterOptions('editor')}
|
||||
options={{
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
export let tabid;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
const timerLabel = useTimerLabel();
|
||||
|
||||
let runnerId;
|
||||
@@ -82,7 +82,7 @@
|
||||
invalidateCommands();
|
||||
}
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
113
packages/web/src/tabs/SqlObjectTab.svelte
Normal file
113
packages/web/src/tabs/SqlObjectTab.svelte
Normal file
@@ -0,0 +1,113 @@
|
||||
<script lang="ts" context="module">
|
||||
const getCurrentEditor = () => getActiveComponent('SqlObjectTab');
|
||||
|
||||
registerCommand({
|
||||
id: 'sqlObject.find',
|
||||
category: 'SQL Object',
|
||||
name: 'Find',
|
||||
keyText: 'CtrlOrCommand+F',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().find(),
|
||||
});
|
||||
|
||||
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName', 'objectTypeField'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
import AceEditor from '../query/AceEditor.svelte';
|
||||
import invalidateCommands from '../commands/invalidateCommands';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { extensions } from '../stores';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import applyScriptTemplate, { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
|
||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import { changeTab } from '../utility/common';
|
||||
|
||||
export let tabid;
|
||||
export let appObjectData;
|
||||
export let scriptTemplate;
|
||||
|
||||
export let schemaName;
|
||||
export let pureName;
|
||||
export let conid;
|
||||
export let database;
|
||||
export let objectTypeField;
|
||||
|
||||
$: appObjectData = {
|
||||
schemaName,
|
||||
pureName,
|
||||
conid,
|
||||
database,
|
||||
objectTypeField,
|
||||
};
|
||||
|
||||
$: defaultScriptTemplate = getSupportedScriptTemplates(appObjectData.objectTypeField)?.[0]?.scriptTemplate;
|
||||
|
||||
$: connection = useConnectionInfo({ conid });
|
||||
$: driver = findEngineDriver($connection, $extensions);
|
||||
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('SqlObjectTab', false);
|
||||
|
||||
let domEditor;
|
||||
let domToolStrip;
|
||||
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
export function find() {
|
||||
domEditor.getEditor().execCommand('find');
|
||||
}
|
||||
|
||||
function createMenu() {
|
||||
return [{ command: 'sqlObject.find' }];
|
||||
}
|
||||
</script>
|
||||
|
||||
<ToolStripContainer>
|
||||
{#await applyScriptTemplate(scriptTemplate ?? defaultScriptTemplate, $extensions, appObjectData)}
|
||||
<LoadingInfo message="Loading script..." />
|
||||
{:then sql}
|
||||
<AceEditor
|
||||
value={sql || ''}
|
||||
readOnly
|
||||
menu={createMenu()}
|
||||
on:focus={() => {
|
||||
activator.activate();
|
||||
domToolStrip?.activate();
|
||||
invalidateCommands();
|
||||
}}
|
||||
bind:this={domEditor}
|
||||
mode={driver?.editorMode || 'sql'}
|
||||
/>
|
||||
{/await}
|
||||
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<SelectField
|
||||
isNative
|
||||
value={scriptTemplate ?? defaultScriptTemplate}
|
||||
options={getSupportedScriptTemplates(appObjectData.objectTypeField).map(x => ({
|
||||
label: x.label,
|
||||
value: x.scriptTemplate,
|
||||
}))}
|
||||
on:change={e => {
|
||||
changeTab(tabid, tab => ({
|
||||
...tab,
|
||||
props: {
|
||||
...tab.props,
|
||||
scriptTemplate: e.detail,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
@@ -29,13 +29,13 @@
|
||||
|
||||
export let tabid;
|
||||
|
||||
const tabVisible: any = getContext('tabVisible');
|
||||
const tabFocused: any = getContext('tabFocused');
|
||||
|
||||
export const activator = createActivator('YamlEditorTab', false);
|
||||
|
||||
let domEditor;
|
||||
|
||||
$: if ($tabVisible && domEditor) {
|
||||
$: if ($tabFocused && domEditor) {
|
||||
domEditor?.getEditor()?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import * as ServerSummaryTab from './ServerSummaryTab.svelte';
|
||||
import * as ProfilerTab from './ProfilerTab.svelte';
|
||||
import * as DataDuplicatorTab from './DataDuplicatorTab.svelte';
|
||||
import * as ImportExportTab from './ImportExportTab.svelte';
|
||||
import * as SqlObjectTab from './SqlObjectTab.svelte';
|
||||
|
||||
import protabs from './index-pro';
|
||||
|
||||
@@ -62,5 +63,6 @@ export default {
|
||||
ProfilerTab,
|
||||
DataDuplicatorTab,
|
||||
ImportExportTab,
|
||||
SqlObjectTab,
|
||||
...protabs,
|
||||
};
|
||||
|
||||
@@ -63,3 +63,79 @@ export default async function applyScriptTemplate(scriptTemplate, extensions, pr
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function getSupportedScriptTemplates(objectTypeField: string): { label: string; scriptTemplate: string }[] {
|
||||
switch (objectTypeField) {
|
||||
case 'tables':
|
||||
return [
|
||||
{ label: 'CREATE TABLE', scriptTemplate: 'CREATE TABLE' },
|
||||
{ label: 'SELECT', scriptTemplate: 'SELECT' },
|
||||
];
|
||||
case 'views':
|
||||
return [
|
||||
{
|
||||
label: 'CREATE VIEW',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'ALTER VIEW',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'CREATE TABLE',
|
||||
scriptTemplate: 'CREATE TABLE',
|
||||
},
|
||||
{
|
||||
label: 'SELECT',
|
||||
scriptTemplate: 'SELECT',
|
||||
},
|
||||
];
|
||||
case 'matviews':
|
||||
return [
|
||||
{
|
||||
label: 'CREATE MATERIALIZED VIEW',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'ALTER MATERIALIZED VIEW',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'CREATE TABLE',
|
||||
scriptTemplate: 'CREATE TABLE',
|
||||
},
|
||||
{
|
||||
label: 'SELECT',
|
||||
scriptTemplate: 'SELECT',
|
||||
},
|
||||
];
|
||||
|
||||
case 'procedures':
|
||||
return [
|
||||
{
|
||||
label: 'CREATE PROCEDURE',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'ALTER PROCEDURE',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
{
|
||||
label: 'EXECUTE',
|
||||
scriptTemplate: 'EXECUTE PROCEDURE',
|
||||
},
|
||||
];
|
||||
|
||||
case 'functions':
|
||||
return [
|
||||
{
|
||||
label: 'CREATE FUNCTION',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
{
|
||||
label: ' ALTER FUNCTION',
|
||||
scriptTemplate: 'ALTER OBJECT',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,81 @@
|
||||
import _ from 'lodash';
|
||||
import { currentDatabase, getCurrentDatabase, getLockedDatabaseMode, openedTabs } from '../stores';
|
||||
import {
|
||||
currentDatabase,
|
||||
getActiveTab,
|
||||
getCurrentDatabase,
|
||||
getLockedDatabaseMode,
|
||||
openedTabs,
|
||||
selectedDatabaseObjectAppObject,
|
||||
} from '../stores';
|
||||
import { shouldShowTab } from '../tabpanel/TabsPanel.svelte';
|
||||
import { callWhenAppLoaded, getAppLoaded } from './appLoadManager';
|
||||
import { getConnectionInfo } from './metadataLoaders';
|
||||
import { switchCurrentDatabase } from './common';
|
||||
|
||||
let lastCurrentTab = null;
|
||||
// let lastCurrentTab = null;
|
||||
|
||||
openedTabs.subscribe(value => {
|
||||
const newCurrentTab = (value || []).find(x => x.selected);
|
||||
if (newCurrentTab == lastCurrentTab) return;
|
||||
if (getLockedDatabaseMode() && getCurrentDatabase()) return;
|
||||
// openedTabs.subscribe(value => {
|
||||
// const newCurrentTab = (value || []).find(x => x.selected);
|
||||
// if (newCurrentTab == lastCurrentTab) return;
|
||||
// if (getLockedDatabaseMode() && getCurrentDatabase()) return;
|
||||
|
||||
const lastTab = lastCurrentTab;
|
||||
lastCurrentTab = newCurrentTab;
|
||||
// if (lastTab?.tabComponent == 'ConnectionTab') return;
|
||||
// const lastTab = lastCurrentTab;
|
||||
// lastCurrentTab = newCurrentTab;
|
||||
// // if (lastTab?.tabComponent == 'ConnectionTab') return;
|
||||
|
||||
if (newCurrentTab) {
|
||||
const { conid, database } = newCurrentTab.props || {};
|
||||
if (conid && database && (conid != lastTab?.props?.conid || database != lastTab?.props?.database)) {
|
||||
const doWork = async () => {
|
||||
const connection = await getConnectionInfo({ conid });
|
||||
switchCurrentDatabase({
|
||||
connection,
|
||||
name: database,
|
||||
});
|
||||
};
|
||||
callWhenAppLoaded(doWork);
|
||||
}
|
||||
// if (newCurrentTab) {
|
||||
// const { conid, database } = newCurrentTab.props || {};
|
||||
// if (conid && database && (conid != lastTab?.props?.conid || database != lastTab?.props?.database)) {
|
||||
// const doWork = async () => {
|
||||
// const connection = await getConnectionInfo({ conid });
|
||||
// switchCurrentDatabase({
|
||||
// connection,
|
||||
// name: database,
|
||||
// });
|
||||
// };
|
||||
// callWhenAppLoaded(doWork);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
export async function handleAfterTabClick() {
|
||||
const currentTab = getActiveTab();
|
||||
const { conid, database, objectTypeField, pureName, schemaName, defaultActionId } = currentTab?.props || {};
|
||||
const db = getCurrentDatabase();
|
||||
if (conid && database && (conid != db?.connection?._id || database != db?.name)) {
|
||||
const connection = await getConnectionInfo({ conid });
|
||||
switchCurrentDatabase({
|
||||
connection,
|
||||
name: database,
|
||||
});
|
||||
// const doWork = async () => {
|
||||
// const connection = await getConnectionInfo({ conid });
|
||||
// switchCurrentDatabase({
|
||||
// connection,
|
||||
// name: database,
|
||||
// });
|
||||
// };
|
||||
// callWhenAppLoaded(doWork);
|
||||
}
|
||||
});
|
||||
|
||||
if (conid && database && objectTypeField && pureName && defaultActionId) {
|
||||
selectedDatabaseObjectAppObject.set({
|
||||
conid,
|
||||
database,
|
||||
objectTypeField,
|
||||
pureName,
|
||||
schemaName,
|
||||
});
|
||||
}
|
||||
|
||||
// focus current tab
|
||||
openedTabs.update(tabs => {
|
||||
return tabs.map(tab => ({
|
||||
...tab,
|
||||
focused: !!tab.selected && !tab.closedTime,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
currentDatabase.subscribe(currentDb => {
|
||||
if (!getLockedDatabaseMode()) return;
|
||||
|
||||
@@ -30,10 +30,23 @@ export function markTabSaved(tabid) {
|
||||
openedTabs.update(files => files.map(tab => (tab.tabid == tabid ? { ...tab, unsaved: false } : tab)));
|
||||
}
|
||||
|
||||
export function setSelectedTabFunc(files, tabid) {
|
||||
export function setSelectedTabFunc(files, tabid, additionalProps = {}) {
|
||||
return [
|
||||
...(files || []).filter(x => x.tabid != tabid).map(x => ({ ...x, selected: false })),
|
||||
...(files || []).filter(x => x.tabid == tabid).map(x => ({ ...x, selected: true })),
|
||||
...(files || [])
|
||||
.filter(x => x.tabid != tabid)
|
||||
.map(x => ({
|
||||
...x,
|
||||
selected: false,
|
||||
focused: false,
|
||||
})),
|
||||
...(files || [])
|
||||
.filter(x => x.tabid == tabid)
|
||||
.map(x => ({
|
||||
...x,
|
||||
selected: true,
|
||||
focused: false,
|
||||
...additionalProps,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ export default async function openNewTab(newTab, initialData: any = undefined, o
|
||||
let existing = null;
|
||||
|
||||
const { savedFile, savedFolder, savedFilePath } = newTab.props || {};
|
||||
const { tabPreviewMode } = newTab;
|
||||
if (savedFile || savedFilePath) {
|
||||
existing = oldTabs.find(
|
||||
x =>
|
||||
@@ -49,7 +50,9 @@ export default async function openNewTab(newTab, initialData: any = undefined, o
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
openedTabs.update(tabs => setSelectedTabFunc(tabs, existing.tabid));
|
||||
openedTabs.update(tabs =>
|
||||
setSelectedTabFunc(tabs, existing.tabid, !tabPreviewMode ? { tabPreviewMode: false } : {})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,8 +95,14 @@ export default async function openNewTab(newTab, initialData: any = undefined, o
|
||||
items.push(newItem);
|
||||
}
|
||||
|
||||
const filesFiltered = tabPreviewMode ? (files || []).filter(x => !x.tabPreviewMode) : files;
|
||||
|
||||
return [
|
||||
...(files || []).map(x => ({ ...x, selected: false, tabOrder: _.findIndex(items, y => y.tabid == x.tabid) })),
|
||||
...(filesFiltered || []).map(x => ({
|
||||
...x,
|
||||
selected: false,
|
||||
tabOrder: _.findIndex(items, y => y.tabid == x.tabid),
|
||||
})),
|
||||
{
|
||||
...newTab,
|
||||
tabid,
|
||||
|
||||
111
packages/web/src/widgets/AppObjectListHandler.svelte
Normal file
111
packages/web/src/widgets/AppObjectListHandler.svelte
Normal file
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
import keycodes from '../utility/keycodes';
|
||||
import _ from 'lodash';
|
||||
|
||||
export let list;
|
||||
export let selectedObjectStore;
|
||||
export let getSelectedObject;
|
||||
export let selectedObjectMatcher;
|
||||
export let module;
|
||||
|
||||
export let onScrollTop = null;
|
||||
export let onFocusFilterBox = null;
|
||||
|
||||
let isListFocused = false;
|
||||
let domDiv = null;
|
||||
|
||||
function handleKeyDown(ev) {
|
||||
function selectByDiff(diff) {
|
||||
const selected = getSelectedObject();
|
||||
const index = _.findIndex(list, x => selectedObjectMatcher(x, selected));
|
||||
|
||||
if (index == 0 && diff < 0) {
|
||||
onFocusFilterBox?.();
|
||||
return;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
let newIndex = index + diff;
|
||||
if (newIndex >= list.length) {
|
||||
newIndex = list.length - 1;
|
||||
}
|
||||
if (newIndex < 0) {
|
||||
newIndex = 0;
|
||||
}
|
||||
|
||||
if (list[newIndex]) {
|
||||
selectedObjectStore.set(list[newIndex]);
|
||||
module.handleObjectClick(list[newIndex], { tabPreviewMode: true });
|
||||
}
|
||||
|
||||
if (newIndex == 0) {
|
||||
onScrollTop?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.keyCode == keycodes.upArrow) {
|
||||
selectByDiff(-1);
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.downArrow) {
|
||||
selectByDiff(1);
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.enter) {
|
||||
module.handleObjectClick(getSelectedObject(), { tabPreviewMode: false, focusTab: true });
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.pageDown) {
|
||||
selectByDiff(10);
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.pageUp) {
|
||||
selectByDiff(-10);
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.home) {
|
||||
if (list[0]) {
|
||||
selectedObjectStore.set(list[0]);
|
||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
||||
onScrollTop?.();
|
||||
}
|
||||
}
|
||||
if (ev.keyCode == keycodes.end) {
|
||||
if (list[list.length - 1]) {
|
||||
selectedObjectStore.set(list[list.length - 1]);
|
||||
module.handleObjectClick(list[list.length - 1], { tabPreviewMode: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function focusFirst() {
|
||||
domDiv?.focus();
|
||||
if (list[0]) {
|
||||
selectedObjectStore.set(list[0]);
|
||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
||||
onScrollTop?.();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
tabindex="0"
|
||||
on:keydown={handleKeyDown}
|
||||
class="wrapper"
|
||||
class:app-object-list-focused={isListFocused}
|
||||
on:focus={() => {
|
||||
isListFocused = true;
|
||||
}}
|
||||
on:blur={() => {
|
||||
isListFocused = false;
|
||||
}}
|
||||
bind:this={domDiv}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper:focus {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
@@ -36,18 +36,28 @@
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import { currentDatabase, extensions } from '../stores';
|
||||
import {
|
||||
currentDatabase,
|
||||
extensions,
|
||||
getSelectedDatabaseObjectAppObject,
|
||||
selectedDatabaseObjectAppObject,
|
||||
} from '../stores';
|
||||
import newQuery from '../query/newQuery';
|
||||
import runCommand from '../commands/runCommand';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { filterAppsForDatabase } from '../utility/appTools';
|
||||
import SchemaSelector from './SchemaSelector.svelte';
|
||||
import { appliedCurrentSchema } from '../stores';
|
||||
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
||||
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
|
||||
let filter = '';
|
||||
let domContainer = null;
|
||||
let domFilter = null;
|
||||
let domListHandler;
|
||||
|
||||
$: objects = useDatabaseInfo({ conid, database });
|
||||
$: status = useDatabaseStatus({ conid, database });
|
||||
@@ -151,7 +161,14 @@
|
||||
</WidgetsInnerContainer>
|
||||
{:else}
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search in tables, objects, # prefix in columns" bind:value={filter} />
|
||||
<SearchInput
|
||||
placeholder="Search in tables, objects, # prefix in columns"
|
||||
bind:value={filter}
|
||||
bind:this={domFilter}
|
||||
onFocusFilteredList={() => {
|
||||
domListHandler?.focusFirst();
|
||||
}}
|
||||
/>
|
||||
<CloseSearchButton bind:filter />
|
||||
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
||||
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
||||
@@ -168,27 +185,42 @@
|
||||
negativeMarginTop
|
||||
/>
|
||||
|
||||
<WidgetsInnerContainer>
|
||||
<WidgetsInnerContainer bind:this={domContainer}>
|
||||
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
||||
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
||||
{:else}
|
||||
<AppObjectList
|
||||
list={objectList
|
||||
.filter(x => ($appliedCurrentSchema ? x.schemaName == $appliedCurrentSchema : true))
|
||||
.map(x => ({ ...x, conid, database }))}
|
||||
<AppObjectListHandler
|
||||
bind:this={domListHandler}
|
||||
list={flatFilteredList.map(x => ({ ...x, conid, database }))}
|
||||
selectedObjectStore={selectedDatabaseObjectAppObject}
|
||||
getSelectedObject={getSelectedDatabaseObjectAppObject}
|
||||
selectedObjectMatcher={matchDatabaseObjectAppObject}
|
||||
module={databaseObjectAppObject}
|
||||
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
||||
subItemsComponent={SubColumnParamList}
|
||||
isExpandable={data =>
|
||||
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'}
|
||||
expandIconFunc={chevronExpandIcon}
|
||||
{filter}
|
||||
passProps={{
|
||||
showPinnedInsteadOfUnpin: true,
|
||||
connection: $connection,
|
||||
hideSchemaName: !!$appliedCurrentSchema,
|
||||
onScrollTop={() => {
|
||||
domContainer?.scrollTop();
|
||||
}}
|
||||
/>
|
||||
onFocusFilterBox={() => {
|
||||
domFilter?.focus();
|
||||
}}
|
||||
>
|
||||
<AppObjectList
|
||||
list={objectList
|
||||
.filter(x => ($appliedCurrentSchema ? x.schemaName == $appliedCurrentSchema : true))
|
||||
.map(x => ({ ...x, conid, database }))}
|
||||
module={databaseObjectAppObject}
|
||||
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
||||
subItemsComponent={SubColumnParamList}
|
||||
isExpandable={data =>
|
||||
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'}
|
||||
expandIconFunc={chevronExpandIcon}
|
||||
{filter}
|
||||
passProps={{
|
||||
showPinnedInsteadOfUnpin: true,
|
||||
connection: $connection,
|
||||
hideSchemaName: !!$appliedCurrentSchema,
|
||||
}}
|
||||
/>
|
||||
</AppObjectListHandler>
|
||||
{/if}
|
||||
</WidgetsInnerContainer>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<div on:drop><slot /></div>
|
||||
<script lang="ts">
|
||||
let domDiv;
|
||||
|
||||
export function scrollTop() {
|
||||
domDiv.scrollTop = 0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div on:drop bind:this={domDiv}><slot /></div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
|
||||
Reference in New Issue
Block a user