antares/src/renderer/components/WorkspaceExploreBarSchema.vue

595 lines
22 KiB
Vue
Raw Normal View History

2020-06-03 20:56:44 +02:00
<template>
<details ref="schemaAccordion" class="accordion workspace-explorebar-database">
2020-06-05 21:00:15 +02:00
<summary
class="accordion-header database-name"
2020-06-15 18:23:51 +02:00
:class="{'text-bold': breadcrumbs.schema === database.name}"
@click="selectSchema(database.name)"
@contextmenu.prevent="showSchemaContext($event, database.name)"
2020-06-05 21:00:15 +02:00
>
<div v-if="isLoading" class="icon loading" />
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
2020-08-12 18:11:48 +02:00
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
2021-11-06 16:36:54 +01:00
<div class="">
<span>{{ database.name }}</span>
<div
v-if="database.size"
class="schema-size tooltip tooltip-left mr-1"
:data-tooltip="formatBytes(database.size)"
>
<i class="mdi mdi-information-outline pr-2" />
</div>
</div>
2020-06-03 20:56:44 +02:00
</summary>
<div class="accordion-body">
2020-06-10 19:29:10 +02:00
<div class="database-tables">
2020-06-03 20:56:44 +02:00
<ul class="menu menu-nav pt-0">
<li
2021-02-20 11:55:34 +01:00
v-for="table of filteredTables"
:ref="breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name) ? 'explorebar-selected' : ''"
2020-10-12 18:45:15 +02:00
:key="table.name"
2020-06-03 20:56:44 +02:00
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
@mousedown.left="selectTable({schema: database.name, table})"
2021-07-13 19:23:02 +02:00
@dblclick="openDataTab({schema: database.name, table})"
2020-12-26 14:47:15 +01:00
@contextmenu.prevent="showTableContext($event, table)"
2020-06-03 20:56:44 +02:00
>
<a class="table-name">
<div v-if="checkLoadingStatus(table.name, 'table')" class="icon loading mr-1" />
<i
v-else
class="table-icon mdi mdi-18px mr-1"
:class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'"
/>
2021-02-20 11:55:34 +01:00
<span v-html="highlightWord(table.name)" />
2020-06-03 20:56:44 +02:00
</a>
2020-12-27 16:16:48 +01:00
<div
v-if="table.type === 'table' && table.size !== false"
2020-12-27 16:16:48 +01:00
class="table-size tooltip tooltip-left mr-1"
:data-tooltip="formatBytes(table.size)"
>
<div class="pie" :style="piePercentage(table.size)" />
</div>
2020-06-03 20:56:44 +02:00
</li>
</ul>
</div>
2020-12-31 19:55:02 +01:00
<div v-if="filteredTriggers.length && customizations.triggers" class="database-misc">
2020-12-31 19:55:02 +01:00
<details class="accordion">
<summary
class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}"
@contextmenu.prevent="showMiscFolderContext($event, 'trigger')"
>
2020-12-31 19:55:02 +01:00
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
{{ $tc('word.trigger', 2) }}
</summary>
<div class="accordion-body">
<div>
<ul class="menu menu-nav pt-0">
<li
2021-02-20 11:55:34 +01:00
v-for="trigger of filteredTriggers"
2020-12-31 19:55:02 +01:00
:key="trigger.name"
:ref="breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name ? 'explorebar-selected' : ''"
2020-12-31 19:55:02 +01:00
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
@mousedown.left="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
2021-07-17 13:10:54 +02:00
@dblclick="openMiscPermanentTab({schema: database.name, misc: trigger, type: 'trigger'})"
2021-01-02 14:46:27 +01:00
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
2020-12-31 19:55:02 +01:00
>
<a class="table-name">
<div v-if="checkLoadingStatus(trigger.name, 'trigger')" class="icon loading mr-1" />
<i v-else class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
2021-02-20 11:55:34 +01:00
<span v-html="highlightWord(trigger.name)" />
2020-12-31 19:55:02 +01:00
</a>
<div
v-if="trigger.enabled === false"
class="tooltip tooltip-left disabled-indicator"
:data-tooltip="$t('word.disabled')"
>
<i class="table-icon mdi mdi-pause mdi-18px mr-1" />
</div>
2020-12-31 19:55:02 +01:00
</li>
</ul>
</div>
</div>
</details>
</div>
<div v-if="filteredProcedures.length && customizations.routines" class="database-misc">
2020-12-31 19:55:02 +01:00
<details class="accordion">
<summary
class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}"
@contextmenu.prevent="showMiscFolderContext($event, 'procedure')"
>
2021-01-05 17:25:18 +01:00
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
2020-12-31 19:55:02 +01:00
{{ $tc('word.storedRoutine', 2) }}
</summary>
<div class="accordion-body">
<div>
<ul class="menu menu-nav pt-0">
<li
v-for="(procedure, i) of filteredProcedures"
:key="`${procedure.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name ? 'explorebar-selected' : ''"
2020-12-31 19:55:02 +01:00
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}"
@mousedown.left="selectMisc({schema: database.name, misc: procedure, type: 'routine'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})"
2021-01-02 14:46:27 +01:00
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
2020-12-31 19:55:02 +01:00
>
<a class="table-name">
2021-01-05 17:25:18 +01:00
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
2021-02-20 11:55:34 +01:00
<span v-html="highlightWord(procedure.name)" />
2020-12-31 19:55:02 +01:00
</a>
</li>
</ul>
</div>
</div>
2021-01-05 17:25:18 +01:00
</details>
</div>
<div v-if="filteredTriggerFunctions.length && customizations.triggerFunctions" class="database-misc">
<details class="accordion">
<summary
class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction}"
@contextmenu.prevent="showMiscFolderContext($event, 'triggerFunction')"
>
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
{{ $tc('word.triggerFunction', 2) }}
</summary>
<div class="accordion-body">
<div>
<ul class="menu menu-nav pt-0">
<li
v-for="(func, i) of filteredTriggerFunctions"
:key="`${func.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name ? 'explorebar-selected' : ''"
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
2021-07-19 22:38:56 +02:00
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'triggerFunction'})"
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
>
<a class="table-name">
<i class="table-icon mdi mdi-cog-clockwise mdi-18px mr-1" />
<span v-html="highlightWord(func.name)" />
</a>
</li>
</ul>
</div>
</div>
</details>
</div>
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
2021-01-05 17:25:18 +01:00
<details class="accordion">
<summary
class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}"
@contextmenu.prevent="showMiscFolderContext($event, 'function')"
>
2021-01-05 17:25:18 +01:00
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
{{ $tc('word.function', 2) }}
</summary>
<div class="accordion-body">
<div>
<ul class="menu menu-nav pt-0">
<li
v-for="(func, i) of filteredFunctions"
:key="`${func.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.function === func.name ? 'explorebar-selected' : ''"
2021-01-05 17:25:18 +01:00
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'function'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'function'})"
2021-01-05 17:25:18 +01:00
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
>
<a class="table-name">
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
2021-02-20 11:55:34 +01:00
<span v-html="highlightWord(func.name)" />
2021-01-05 17:25:18 +01:00
</a>
</li>
</ul>
</div>
</div>
2020-12-31 19:55:02 +01:00
</details>
</div>
<div v-if="filteredSchedulers.length && customizations.schedulers" class="database-misc">
2020-12-31 19:55:02 +01:00
<details class="accordion">
<summary
class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}"
@contextmenu.prevent="showMiscFolderContext($event, 'scheduler')"
>
2020-12-31 19:55:02 +01:00
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
{{ $tc('word.scheduler', 2) }}
</summary>
<div class="accordion-body">
<div>
<ul class="menu menu-nav pt-0">
<li
2021-02-20 11:55:34 +01:00
v-for="scheduler of filteredSchedulers"
2020-12-31 19:55:02 +01:00
:key="scheduler.name"
:ref="breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name ? 'explorebar-selected' : ''"
2020-12-31 19:55:02 +01:00
class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
@mousedown.left="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: scheduler, type: 'scheduler'})"
2021-01-02 14:46:27 +01:00
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
2020-12-31 19:55:02 +01:00
>
<a class="table-name">
<div v-if="checkLoadingStatus(scheduler.name, 'scheduler')" class="icon loading mr-1" />
<i v-else class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
2021-02-20 11:55:34 +01:00
<span v-html="highlightWord(scheduler.name)" />
2020-12-31 19:55:02 +01:00
</a>
<div
v-if="scheduler.enabled === false"
class="tooltip tooltip-left disabled-indicator"
:data-tooltip="$t('word.disabled')"
>
<i class="table-icon mdi mdi-pause mdi-18px mr-1" />
</div>
2020-12-31 19:55:02 +01:00
</li>
</ul>
</div>
</div>
</details>
</div>
2020-06-03 20:56:44 +02:00
</div>
</details>
</template>
<script>
2022-04-30 00:47:37 +02:00
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import { formatBytes } from 'common/libs/formatBytes';
2022-04-30 00:47:37 +02:00
import { storeToRefs } from 'pinia';
2020-06-05 21:00:15 +02:00
// TODO: expose selectSchema & schemaAccordion
2020-06-03 20:56:44 +02:00
export default {
2021-03-17 11:15:14 +01:00
name: 'WorkspaceExploreBarSchema',
2020-06-03 20:56:44 +02:00
props: {
2020-06-05 21:00:15 +02:00
database: Object,
connection: Object
},
2022-04-30 00:47:37 +02:00
emits: [
'show-schema-context',
'show-table-context',
'show-misc-context',
'show-misc-folder-context'
],
setup () {
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { applicationTheme } = storeToRefs(settingsStore);
const {
getLoadedSchemas,
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
} = workspacesStore;
return {
applicationTheme,
getLoadedSchemas,
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
};
},
data () {
return {
isLoading: false
};
},
2020-06-05 21:00:15 +02:00
computed: {
2021-02-20 11:55:34 +01:00
searchTerm () {
return this.getSearchTerm(this.connection.uid);
},
filteredTables () {
return this.database.tables.filter(table => table.name.search(this.searchTerm) >= 0);
},
filteredTriggers () {
return this.database.triggers.filter(trigger => trigger.name.search(this.searchTerm) >= 0);
},
filteredProcedures () {
return this.database.procedures.filter(procedure => procedure.name.search(this.searchTerm) >= 0);
},
filteredFunctions () {
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
},
filteredTriggerFunctions () {
return this.database.triggerFunctions
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
: [];
},
2021-02-20 11:55:34 +01:00
filteredSchedulers () {
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
},
workspace () {
return this.getWorkspace(this.connection.uid);
},
2020-06-05 21:00:15 +02:00
breadcrumbs () {
return this.workspace.breadcrumbs;
},
customizations () {
return this.workspace.customizations;
},
loadedSchemas () {
return this.getLoadedSchemas(this.connection.uid);
},
maxSize () {
return this.database.tables.reduce((acc, curr) => {
if (curr.size > acc) acc = curr.size;
return acc;
}, 0);
},
totalSize () {
return this.database.tables.reduce((acc, curr) => acc + curr.size, 0);
2020-06-05 21:00:15 +02:00
}
},
watch: {
breadcrumbs (newVal, oldVal) {
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
setTimeout(() => {
const element = this.$refs['explorebar-selected'] ? this.$refs['explorebar-selected'][0] : null;
if (element) {
const rect = element.getBoundingClientRect();
const elemTop = rect.top;
const elemBottom = rect.bottom;
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
if (!isVisible) {
element.setAttribute('tabindex', '-1');
element.focus();
element.removeAttribute('tabindex');
}
}
}, 50);
}
}
},
2020-06-05 21:00:15 +02:00
methods: {
formatBytes,
async selectSchema (schema) {
if (!this.loadedSchemas.has(schema) && !this.isLoading) {
this.isLoading = true;
await this.refreshSchema({ uid: this.connection.uid, schema });
2021-07-21 17:56:55 +02:00
this.addLoadedSchema(schema);
this.isLoading = false;
}
},
2021-07-12 19:18:29 +02:00
selectTable ({ schema, table }) {
this.newTab({
uid: this.connection.uid,
elementName: table.name,
schema: this.database.name,
type: 'temp-data',
elementType: table.type
});
2021-07-13 19:23:02 +02:00
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
2021-07-17 13:10:54 +02:00
selectMisc ({ schema, misc, type }) {
const miscTempTabs = {
2021-07-19 22:38:56 +02:00
trigger: 'temp-trigger-props',
triggerFunction: 'temp-trigger-function-props',
function: 'temp-function-props',
routine: 'temp-routine-props',
scheduler: 'temp-scheduler-props'
2021-07-17 13:10:54 +02:00
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTempTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
2021-07-13 19:23:02 +02:00
openDataTab ({ schema, table }) {
2021-07-16 18:52:18 +02:00
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
2021-07-13 19:23:02 +02:00
this.setBreadcrumbs({ schema, [table.type]: table.name });
2021-07-12 19:18:29 +02:00
},
2021-07-17 13:10:54 +02:00
openMiscPermanentTab ({ schema, misc, type }) {
const miscTabs = {
2021-07-19 22:38:56 +02:00
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
2021-07-17 13:10:54 +02:00
};
2021-07-17 16:09:57 +02:00
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTabs[type],
elementType: type
});
2021-07-17 13:10:54 +02:00
this.setBreadcrumbs({ schema, [type]: misc.name });
},
showSchemaContext (event, schema) {
this.$emit('show-schema-context', { event, schema });
2020-10-01 15:08:35 +02:00
},
2020-12-03 13:00:54 +01:00
showTableContext (event, table) {
this.$emit('show-table-context', { event, schema: this.database.name, table });
},
2021-01-02 14:46:27 +01:00
showMiscContext (event, misc) {
2021-07-17 13:10:54 +02:00
this.$emit('show-misc-context', { event, schema: this.database.name, misc });
2021-01-02 14:46:27 +01:00
},
showMiscFolderContext (event, type) {
2021-07-17 13:10:54 +02:00
this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
},
piePercentage (val) {
const perc = val / this.maxSize * 100;
2021-04-03 11:21:58 +02:00
if (this.applicationTheme === 'dark')
return { background: `conic-gradient(lime ${perc}%, white 0)` };
else
return { background: `conic-gradient(teal ${perc}%, silver 0)` };
2020-12-04 11:19:16 +01:00
},
setBreadcrumbs (payload) {
if (this.breadcrumbs.schema === payload.schema && this.breadcrumbs.table === payload.table) return;
this.changeBreadcrumbs(payload);
2021-02-20 11:55:34 +01:00
},
highlightWord (string) {
string = string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
2021-02-20 11:55:34 +01:00
if (this.searchTerm) {
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return string.replace(regexp, '<span class="text-primary">$1</span>');
}
else
return string;
},
checkLoadingStatus (name, type) {
return this.workspace.loadingElements.some(el =>
el.name === name &&
el.type === type &&
el.schema === this.database.name);
2020-10-01 15:08:35 +02:00
}
2020-06-03 20:56:44 +02:00
}
};
</script>
<style lang="scss">
.workspace-explorebar-database {
.selected {
font-weight: 700;
}
.database-name {
position: sticky;
top: 0;
z-index: 2;
2021-11-06 16:36:54 +01:00
.schema-size {
visibility: hidden;
width: 22.5px;
2021-11-06 16:36:54 +01:00
}
}
.database-name,
2020-12-31 19:55:02 +01:00
.misc-name,
a.table-name {
display: flex;
align-items: center;
padding: 0.1rem 1rem 0.1rem 0.1rem;
cursor: pointer;
font-size: 0.7rem;
2020-06-03 20:56:44 +02:00
> span {
overflow: hidden;
white-space: nowrap;
display: block;
text-overflow: ellipsis;
}
2020-06-03 20:56:44 +02:00
.database-icon,
2020-12-31 19:55:02 +01:00
.table-icon,
.misc-icon {
opacity: 0.7;
2020-07-31 18:16:28 +02:00
}
.loading {
height: 18px;
width: 18px;
&::after {
height: 0.6rem;
width: 0.6rem;
}
}
}
2020-12-31 19:55:02 +01:00
.misc-name {
line-height: 1;
padding: 0.1rem 1rem 0.1rem 0.1rem;
position: relative;
}
.database-name,
.misc-name {
&:hover {
border-radius: $border-radius;
2021-11-06 16:36:54 +01:00
.schema-size {
visibility: visible;
2021-11-06 16:36:54 +01:00
}
}
}
.menu-item {
line-height: 1.2;
position: relative;
2020-06-10 19:29:10 +02:00
&:hover,
&.selected {
border-radius: $border-radius;
}
}
.database-tables {
margin-left: 1.2rem;
}
2020-12-31 19:55:02 +01:00
.database-misc {
margin-left: 1.6rem;
.accordion[open] .accordion-header > .misc-icon:first-child::before {
content: "\F0770";
}
.accordion-body {
margin-bottom: 0.2rem;
}
}
2021-11-06 16:36:54 +01:00
.schema-size,
.table-size,
.disabled-indicator {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
display: flex;
align-items: center;
height: 100%;
opacity: 0.2;
transition: opacity 0.2s;
&:hover {
opacity: 0.8;
}
&::after {
font-weight: 400;
font-size: 0.5rem;
2020-07-31 18:16:28 +02:00
}
.pie {
width: 14px;
height: 14px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
2020-07-31 18:16:28 +02:00
}
}
}
2020-06-03 20:56:44 +02:00
</style>