mirror of
https://github.com/beekeeper-studio/beekeeper-studio.git
synced 2026-03-13 10:12:54 +08:00
Merge pull request #2969 from beekeeper-studio/fix/status-bar
Add global status bar
This commit is contained in:
@@ -1,19 +1,29 @@
|
||||
@use "sass:color";
|
||||
$button-height: $statusbar-height * 0.72;
|
||||
|
||||
.statusbar {
|
||||
.global-status-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: $statusbar-bg;
|
||||
color: rgba($statusbar-text, 0.87);
|
||||
min-height: $statusbar-height;
|
||||
height: $statusbar-height;
|
||||
line-height: $statusbar-height;
|
||||
font-size: 0.8rem;
|
||||
padding: 0 $gutter-h;
|
||||
// overflow: hidden;
|
||||
|
||||
> .vue-portal-target {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.statusbar {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 $gutter-h;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
/* display: none; <- Crashes Chrome on hover */
|
||||
@@ -22,7 +32,7 @@ $button-height: $statusbar-height * 0.72;
|
||||
}
|
||||
|
||||
|
||||
> .col {
|
||||
.statusbar > .col {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -59,32 +69,20 @@ $button-height: $statusbar-height * 0.72;
|
||||
}
|
||||
|
||||
|
||||
// Row Actions
|
||||
// -------------------------
|
||||
.connection-button > .btn {
|
||||
&:hover {
|
||||
background: rgba(white, 0.1);
|
||||
}
|
||||
.connection-button-wrapper {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.connection-button {
|
||||
height: 100%;
|
||||
> .btn{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-shadow: none!important;
|
||||
background: transparent;
|
||||
color: $statusbar-text;
|
||||
height: $button-height;
|
||||
line-height: $button-height;
|
||||
min-width: min-content;
|
||||
margin: 0 0.15rem;
|
||||
margin-top: -1px;
|
||||
padding: 0 0.35rem;
|
||||
--ripple-background: none;
|
||||
border-radius: $btn-border-radius-small;
|
||||
transition: background 0.15s ease-in-out;
|
||||
.material-icons {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,27 +305,19 @@ $button-height: $statusbar-height * 0.72;
|
||||
|
||||
// Connection Status
|
||||
// ---------------------------
|
||||
.connection-button,
|
||||
.connection-button > .btn {
|
||||
height: $statusbar-height;
|
||||
line-height: $statusbar-height;
|
||||
margin: -$gutter-h;
|
||||
padding: 0 $gutter-h;
|
||||
flex: 1 1 100%;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
}
|
||||
.connection-name {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.connection-button {
|
||||
position: relative;
|
||||
.material-icons {
|
||||
font-size: 20px;
|
||||
min-width: 38px;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
width: calc(var(--connection-button-icon-width) + 0.2rem);
|
||||
}
|
||||
> .btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
--trigger-effect: none;
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
@toggleSidebar="handleToggleOpenPrimarySidebar"
|
||||
:sidebar-shown="primarySidebarOpen"
|
||||
/>
|
||||
<statusbar>
|
||||
<ConnectionButton />
|
||||
</statusbar>
|
||||
</sidebar>
|
||||
<div
|
||||
ref="content"
|
||||
@@ -34,6 +31,10 @@
|
||||
</div>
|
||||
<secondary-sidebar ref="secondarySidebar" @close="handleToggleOpenSecondarySidebar(false)" />
|
||||
</div>
|
||||
<global-status-bar
|
||||
:connection-button-width="primarySidebarWidth"
|
||||
:connection-button-icon-width="$bksConfig.ui.layout.primarySidebarMinWidth"
|
||||
/>
|
||||
<quick-search
|
||||
v-if="quickSearchShown"
|
||||
@close="quickSearchShown=false"
|
||||
@@ -50,26 +51,27 @@
|
||||
import SecondarySidebar from './sidebar/SecondarySidebar.vue'
|
||||
import CoreTabs from './CoreTabs.vue'
|
||||
import Split from 'split.js'
|
||||
import Statusbar from './common/StatusBar.vue'
|
||||
import ConnectionButton from './sidebar/core/ConnectionButton.vue'
|
||||
import ExportManager from './export/ExportManager.vue'
|
||||
import {AppEvent} from '../common/AppEvent'
|
||||
import QuickSearch from './quicksearch/QuickSearch.vue'
|
||||
import ProgressBar from './editor/ProgressBar.vue'
|
||||
import LostConnectionModal from './LostConnectionModal.vue'
|
||||
import GlobalStatusBar from './GlobalStatusBar.vue'
|
||||
import Vue from 'vue'
|
||||
import RenameDatabaseElementModal from './common/modals/RenameDatabaseElementModal.vue'
|
||||
import { mapGetters, mapActions, mapState } from 'vuex'
|
||||
import _ from "lodash"
|
||||
|
||||
export default Vue.extend({
|
||||
components: { CoreSidebar, CoreTabs, Sidebar, Statusbar, ConnectionButton, ExportManager, QuickSearch, ProgressBar, LostConnectionModal, RenameDatabaseElementModal, SecondarySidebar },
|
||||
components: { CoreSidebar, CoreTabs, Sidebar, ExportManager, QuickSearch, ProgressBar, LostConnectionModal, RenameDatabaseElementModal, SecondarySidebar, GlobalStatusBar },
|
||||
data() {
|
||||
/* eslint-disable */
|
||||
return {
|
||||
split: null,
|
||||
quickSearchShown: false,
|
||||
initializing: true,
|
||||
resizeObserver: null,
|
||||
primarySidebarWidth: 0,
|
||||
}
|
||||
/* eslint-enable */
|
||||
},
|
||||
@@ -159,6 +161,17 @@
|
||||
this.registerHandlers(this.rootBindings)
|
||||
this.$nextTick(() => {
|
||||
this.initializing = false
|
||||
// This is the easiest way to track the width of the primary sidebar
|
||||
// in real time because sidebar can be resized by dragging or clicking
|
||||
// the toggle button. An alternative to this would be assigning the
|
||||
// width on drag and click events.
|
||||
this.resizeObserver = new ResizeObserver((entries) => {
|
||||
const primarySidebar = entries[0]
|
||||
this.primarySidebarWidth = primarySidebar.contentRect.width
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.resizeObserver.observe(this.splitElements[0])
|
||||
})
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -170,6 +183,9 @@
|
||||
if(this.split) {
|
||||
this.split.destroy()
|
||||
}
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
<div class="expand layout-center">
|
||||
<shortcut-hints />
|
||||
</div>
|
||||
<statusbar class="tabulator-footer" />
|
||||
</div>
|
||||
<div
|
||||
v-for="(tab, idx) in tabItems"
|
||||
@@ -109,6 +108,7 @@
|
||||
v-if="tab.tabType === 'import-export-database'"
|
||||
:schema="tab.schemaName"
|
||||
:tab="tab"
|
||||
:active="activeTab.id === tab.id"
|
||||
@close="close"
|
||||
/>
|
||||
<DatabaseBackup
|
||||
|
||||
27
apps/studio/src/components/GlobalStatusBar.vue
Normal file
27
apps/studio/src/components/GlobalStatusBar.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="global-status-bar">
|
||||
<div
|
||||
class="connection-button-wrapper"
|
||||
:style="{
|
||||
['--connection-button-icon-width']: connectionButtonIconWidth + 'px',
|
||||
width: connectionButtonWidth + 'px',
|
||||
}"
|
||||
>
|
||||
<connection-button />
|
||||
</div>
|
||||
<portal-target name="global-status-bar"></portal-target>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PortalTarget } from "portal-vue";
|
||||
import ConnectionButton from "./sidebar/core/ConnectionButton.vue";
|
||||
|
||||
export default {
|
||||
components: { PortalTarget, ConnectionButton },
|
||||
props: {
|
||||
connectionButtonWidth: Number,
|
||||
connectionButtonIconWidth: Number,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -102,7 +102,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="expand" />
|
||||
<status-bar>
|
||||
<status-bar :active="active">
|
||||
<div class="statusbar-info col flex expand">
|
||||
<span
|
||||
class="statusbar-item"
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<status-bar>
|
||||
<status-bar :active="active">
|
||||
<div class="statusbar-info col flex expand">
|
||||
<span
|
||||
class="statusbar-item"
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
@submitCurrentQueryToFile="submitCurrentQueryToFile"
|
||||
@wrap-text="wrapText = !wrapText"
|
||||
:execute-time="executeTime"
|
||||
:active="active"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
@clipboardJson="clipboardJson"
|
||||
@clipboardMarkdown="clipboardMarkdown"
|
||||
:execute-time="executeTime"
|
||||
:active="active"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="expand" />
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<span class="expand" />
|
||||
<div class="col flex-right statusbar-actions">
|
||||
<x-buttons class="pending-changes">
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<template>
|
||||
<footer :class="classes">
|
||||
<slot />
|
||||
</footer>
|
||||
<portal to="global-status-bar">
|
||||
<footer :class="classes" v-if="active">
|
||||
<slot />
|
||||
</footer>
|
||||
</portal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
props: ['mode'],
|
||||
props: ['mode', 'active'],
|
||||
computed: {
|
||||
...mapGetters(['connectionColor']),
|
||||
classes() {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<statusbar :class="{ 'empty': results.length === 0, 'query-meta': true }">
|
||||
<statusbar
|
||||
:active="active"
|
||||
:class="{ 'empty': results.length === 0, 'query-meta': true }"
|
||||
>
|
||||
<template v-if="results.length > 0">
|
||||
<div
|
||||
class="truncate statusbar-info"
|
||||
@@ -61,99 +64,95 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="expand" />
|
||||
<span class="empty">No Data</span>
|
||||
</template>
|
||||
<div class="flex flex-right statusbar-right-actions">
|
||||
<x-button
|
||||
class="btn btn-flat btn-icon end"
|
||||
:disabled="results.length === 0"
|
||||
menu
|
||||
>
|
||||
Download <i class="material-icons">arrow_drop_down</i>
|
||||
<x-menu>
|
||||
<x-menuitem @click.prevent="download('csv')">
|
||||
<x-label>Download as CSV</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('xlsx')">
|
||||
<x-label>Download as Excel</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('json')">
|
||||
<x-label>Download as JSON</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('md')">
|
||||
<x-label>Download as Markdown</x-label>
|
||||
</x-menuitem>
|
||||
<span
|
||||
v-if="dialect !== 'mongodb'"
|
||||
v-tooltip="{
|
||||
content: downloadFullTooltip
|
||||
}"
|
||||
>
|
||||
<x-menuitem
|
||||
@click.prevent="$event => submitCurrentQueryToFile()"
|
||||
:disabled="!(result && result.truncated)"
|
||||
>
|
||||
<x-label>Download Full Resultset</x-label>
|
||||
<i
|
||||
v-if="$store.getters.isCommunity"
|
||||
class="material-icons menu-icon"
|
||||
>stars</i>
|
||||
</x-menuitem>
|
||||
</span>
|
||||
<hr>
|
||||
<span v-else class="empty">No Data</span>
|
||||
<span class="expand" />
|
||||
<x-button
|
||||
class="btn btn-flat btn-icon end"
|
||||
:disabled="results.length === 0"
|
||||
menu
|
||||
>
|
||||
Download <i class="material-icons">arrow_drop_down</i>
|
||||
<x-menu>
|
||||
<x-menuitem @click.prevent="download('csv')">
|
||||
<x-label>Download as CSV</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('xlsx')">
|
||||
<x-label>Download as Excel</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('json')">
|
||||
<x-label>Download as JSON</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem @click.prevent="download('md')">
|
||||
<x-label>Download as Markdown</x-label>
|
||||
</x-menuitem>
|
||||
<span
|
||||
v-if="dialect !== 'mongodb'"
|
||||
v-tooltip="{
|
||||
content: downloadFullTooltip
|
||||
}"
|
||||
>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboard"
|
||||
@click.prevent="$event => submitCurrentQueryToFile()"
|
||||
:disabled="!(result && result.truncated)"
|
||||
>
|
||||
<x-label>Copy to Clipboard (TSV / Excel)</x-label>
|
||||
<x-label>Download Full Resultset</x-label>
|
||||
<i
|
||||
v-if="$store.getters.isCommunity"
|
||||
class="material-icons menu-icon"
|
||||
>stars</i>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboardJson"
|
||||
>
|
||||
<x-label>Copy to Clipboard (JSON)</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboardMarkdown"
|
||||
>
|
||||
<x-label>Copy to Clipboard (Markdown)</x-label>
|
||||
</x-menuitem>
|
||||
</x-menu>
|
||||
</x-button>
|
||||
<x-button
|
||||
class="actions-btn btn btn-flat settings-btn"
|
||||
menu
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
<i class="material-icons">arrow_drop_down</i>
|
||||
<x-menu>
|
||||
<x-menuitem disabled togglable>
|
||||
<x-label>Editor keymap</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
:key="t.value"
|
||||
v-for="t in keymapTypes"
|
||||
togglable
|
||||
:toggled="t.value === userKeymap"
|
||||
@click.prevent="userKeymap = t.value"
|
||||
>
|
||||
<x-label>{{ t.name }}</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
togglable
|
||||
:toggled="wrapText"
|
||||
@click.prevent="$emit('wrap-text')"
|
||||
>
|
||||
<x-label class="flex-between">
|
||||
Wrap Text
|
||||
</x-label>
|
||||
</x-menuitem>
|
||||
</x-menu>
|
||||
</x-button>
|
||||
</div>
|
||||
</span>
|
||||
<hr>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboard"
|
||||
>
|
||||
<x-label>Copy to Clipboard (TSV / Excel)</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboardJson"
|
||||
>
|
||||
<x-label>Copy to Clipboard (JSON)</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
title="Probably don't do this with large results (500+)"
|
||||
@click.prevent="copyToClipboardMarkdown"
|
||||
>
|
||||
<x-label>Copy to Clipboard (Markdown)</x-label>
|
||||
</x-menuitem>
|
||||
</x-menu>
|
||||
</x-button>
|
||||
<x-button
|
||||
class="actions-btn btn btn-flat settings-btn"
|
||||
menu
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
<i class="material-icons">arrow_drop_down</i>
|
||||
<x-menu>
|
||||
<x-menuitem disabled togglable>
|
||||
<x-label>Editor keymap</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
:key="t.value"
|
||||
v-for="t in keymapTypes"
|
||||
togglable
|
||||
:toggled="t.value === userKeymap"
|
||||
@click.prevent="userKeymap = t.value"
|
||||
>
|
||||
<x-label>{{ t.name }}</x-label>
|
||||
</x-menuitem>
|
||||
<x-menuitem
|
||||
togglable
|
||||
:toggled="wrapText"
|
||||
@click.prevent="$emit('wrap-text')"
|
||||
>
|
||||
<x-label class="flex-between">
|
||||
Wrap Text
|
||||
</x-label>
|
||||
</x-menuitem>
|
||||
</x-menu>
|
||||
</x-button>
|
||||
</statusbar>
|
||||
</template>
|
||||
<script>
|
||||
@@ -179,7 +178,7 @@ const shortEnglishHumanizer = humanizeDuration.humanizer({
|
||||
});
|
||||
|
||||
export default {
|
||||
props: ['results', 'running', 'value', 'executeTime', 'wrapText'],
|
||||
props: ['results', 'running', 'value', 'executeTime', 'wrapText', 'active'],
|
||||
components: { Statusbar },
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
:button-portal-target="portalName"
|
||||
/>
|
||||
</div>
|
||||
<status-bar class="import-export__footer">
|
||||
<status-bar class="import-export__footer" :active="active">
|
||||
<div class="statusbar-info col flex expand">
|
||||
<span
|
||||
class="statusbar-item"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="connection-button flex flex-middle"
|
||||
class="connection-button"
|
||||
v-if="config"
|
||||
:title="$bks.buildConnectionString(config)"
|
||||
>
|
||||
@@ -40,7 +40,6 @@
|
||||
</x-menuitem>
|
||||
</x-menu>
|
||||
</x-button>
|
||||
|
||||
<portal to="modals">
|
||||
<modal
|
||||
class="vue-dialog beekeeper-modal save-connection-modal"
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
<div class="expand" />
|
||||
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<div class="flex flex-middle flex-right statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<x-button
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
|
||||
<div class="expand" />
|
||||
<status-bar class="tablulator-footer">
|
||||
<status-bar class="tablulator-footer" :active="active">
|
||||
<div class="flex flex-middle statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<x-button
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
<div class="expand" />
|
||||
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<div class="flex flex-middle flex-right statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<x-button
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<div class="expand" />
|
||||
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<div class="flex flex-middle statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<x-button
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<div class="expand" />
|
||||
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<div class="flex flex-middle statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<x-button
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<div class="expand" />
|
||||
|
||||
<status-bar class="tabulator-footer">
|
||||
<status-bar class="tabulator-footer" :active="active">
|
||||
<div class="flex flex-middle flex-right statusbar-actions">
|
||||
<slot name="footer" />
|
||||
<slot name="actions" />
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<statusbar :mode="statusbarMode">
|
||||
<statusbar :mode="statusbarMode" :active="active">
|
||||
<div class="truncate statusbar-info">
|
||||
<x-button
|
||||
@click.prevent="openProperties"
|
||||
|
||||
Reference in New Issue
Block a user