feat: connections sorted by last usage by default and option to pin them

This commit is contained in:
Fabio Di Stasio 2022-06-30 18:20:14 +02:00
parent f632a0f189
commit 36e98e0742
9 changed files with 115 additions and 62 deletions

View File

@ -9,7 +9,7 @@
/> />
<ul class="settingbar-elements"> <ul class="settingbar-elements">
<Draggable <Draggable
v-model="connections" v-model="pinnedConnectionsArr"
:item-key="'uid'" :item-key="'uid'"
@start="isDragging = true" @start="isDragging = true"
@end="dragStop" @end="dragStop"
@ -23,11 +23,26 @@
@contextmenu.prevent="contextMenu($event, element)" @contextmenu.prevent="contextMenu($event, element)"
@mouseover.self="tooltipPosition" @mouseover.self="tooltipPosition"
> >
<i class="settingbar-element-icon dbi" :class="`dbi-${element.client} ${getStatusBadge(element.uid)}`" /> <i class="settingbar-element-icon dbi" :class="[`dbi-${element.client}`, getStatusBadge(element.uid), (pinnedConnections.has(element.uid) ? 'settingbar-element-pin' : false)]" />
<span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(element.uid) }}</span> <span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(element.uid) }}</span>
</li> </li>
</template> </template>
</Draggable> </Draggable>
<div v-if="pinnedConnectionsArr.length" class="divider" />
<li
v-for="connection in unpinnedConnectionsArr"
:key="connection.uid"
class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': connection.uid === selectedWorkspace}"
@click.stop="selectWorkspace(connection.uid)"
@contextmenu.prevent="contextMenu($event, connection)"
@mouseover.self="tooltipPosition"
>
<i class="settingbar-element-icon dbi" :class="[`dbi-${connection.client}`, getStatusBadge(connection.uid)]" />
<span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
</li>
<li <li
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': 'NEW' === selectedWorkspace}" :class="{'selected': 'NEW' === selectedWorkspace}"
@ -56,7 +71,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, Ref, computed } from 'vue'; import { ref, Ref, computed, watch } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useApplicationStore } from '@/stores/application'; import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections'; import { useConnectionsStore } from '@/stores/connections';
@ -74,7 +89,7 @@ const { connections: storedConnections, pinnedConnections, lastConnections } = s
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore); const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { showSettingModal, showScratchpad } = applicationStore; const { showSettingModal, showScratchpad } = applicationStore;
const { getConnectionName, updateConnections } = connectionsStore; const { getConnectionName, updatePinnedConnections } = connectionsStore;
const { getWorkspace, selectWorkspace } = workspacesStore; const { getWorkspace, selectWorkspace } = workspacesStore;
const isLinux = process.platform === 'linux'; const isLinux = process.platform === 'linux';
@ -83,17 +98,31 @@ const isDragging: Ref<boolean> = ref(false);
const contextEvent: Ref<MouseEvent> = ref(null); const contextEvent: Ref<MouseEvent> = ref(null);
const contextConnection: Ref<ConnectionParams> = ref(null); const contextConnection: Ref<ConnectionParams> = ref(null);
const connections = computed({ const pinnedConnectionsArr = computed({
get () { get: () => [...pinnedConnections.value].map(c => storedConnections.value.find(sc => sc.uid === c)).filter(Boolean),
return storedConnections.value; set: (value: ConnectionParams[]) => {
}, const pinnedUid = value.reduce((acc, curr) => {
set (value: ConnectionParams[]) { acc.push(curr.uid);
updateConnections(value); return acc;
}, []);
updatePinnedConnections(pinnedUid);
} }
}); });
const pinnedConnectionsArr = computed(() => storedConnections.value.filter(c => pinnedConnections.value.has(c.uid))); const unpinnedConnectionsArr = computed(() => {
const unpinnedConnectionsArr = computed(() => storedConnections.value.filter(c => !pinnedConnections.value.has(c.uid))); return storedConnections.value
.filter(c => !pinnedConnections.value.has(c.uid))
.map(c => {
const connTime = lastConnections.value.find((lc) => lc.uid === c.uid)?.time || 0;
return { ...c, time: connTime };
})
.sort((a, b) => {
if (a.time < b.time) return 1;
else if (a.time > b.time) return -1;
return 0;
});
});
const hasUpdates = computed(() => ['available', 'downloading', 'downloaded', 'link'].includes(updateStatus.value)); const hasUpdates = computed(() => ['available', 'downloading', 'downloaded', 'link'].includes(updateStatus.value));
@ -129,13 +158,33 @@ const getStatusBadge = (uid: string) => {
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const dragStop = (e: any) => { // TODO: temp const dragStop = (e: any) => {
isDragging.value = false; isDragging.value = false;
setTimeout(() => { setTimeout(() => {
tooltipPosition(e.originalEvent.target.parentNode); tooltipPosition(e.originalEvent.target.parentNode);
}, 200); }, 200);
}; };
watch(unpinnedConnectionsArr, (newVal, oldVal) => {
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
setTimeout(() => {
const element = document.querySelector<HTMLElement>('.settingbar-element.selected');
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);
}
});
</script> </script>
<style lang="scss"> <style lang="scss">
@ -217,6 +266,20 @@ const dragStop = (e: any) => { // TODO: temp
bottom: initial; bottom: initial;
} }
} }
.settingbar-element-pin{
margin: 0 auto;
&::before {
font: normal normal normal 14px/1 "Material Design Icons";
content: "\F0403";
transform: rotate(45deg);
opacity: .25;
bottom: -8px;
left: -4px;
position: absolute;
}
}
} }
} }
} }

View File

@ -125,9 +125,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue'; import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Prop, Ref, ref, watch } from 'vue';
import { Ace } from 'ace-builds'; import { Ace } from 'ace-builds';
import { EventInfos } from 'common/interfaces/antares'; import { ConnectionParams, EventInfos } from 'common/interfaces/antares';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces'; import { useWorkspacesStore } from '@/stores/workspaces';
@ -141,7 +141,7 @@ const { t } = useI18n();
const props = defineProps({ const props = defineProps({
tabUid: String, tabUid: String,
connection: Object, connection: Object as Prop<ConnectionParams>,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
@ -281,7 +281,7 @@ originalScheduler.value = {
state: 'DISABLE' state: 'DISABLE'
}; };
originalScheduler.value = JSON.parse(JSON.stringify(originalScheduler.value)); localScheduler.value = { ...originalScheduler.value };
setTimeout(() => { setTimeout(() => {
resizeQueryEditor(); resizeQueryEditor();

View File

@ -60,12 +60,15 @@ const useFocusTrap = (args?: {disableAutofocus?: boolean}) => {
focusableElements = (trapRef.value as HTMLElement).querySelectorAll( focusableElements = (trapRef.value as HTMLElement).querySelectorAll(
focusableElementsSelector focusableElementsSelector
); );
if (focusableElements.length) {
$firstFocusable = focusableElements[0]; $firstFocusable = focusableElements[0];
$lastFocusable = focusableElements[focusableElements.length - 1]; $lastFocusable = focusableElements[focusableElements.length - 1];
document.addEventListener('keydown', keyHandler); document.addEventListener('keydown', keyHandler);
isInitiated.value = true; isInitiated.value = true;
if (!localArgs.disableAutofocus) $firstFocusable.focus(); if (!localArgs.disableAutofocus) $firstFocusable.focus();
} }
}
function clearFocusTrap () { function clearFocusTrap () {
document.removeEventListener('keydown', keyHandler); document.removeEventListener('keydown', keyHandler);

View File

@ -3,6 +3,7 @@
width: 42px; width: 42px;
height: 42px; height: 42px;
background-size: cover; background-size: cover;
position: relative;
&.dbi-mysql { &.dbi-mysql {
background-image: url("../images/svg/mysql.svg"); background-image: url("../images/svg/mysql.svg");

View File

@ -368,6 +368,10 @@ option:checked {
} }
} }
.divider {
margin: 0.15rem 0.3rem;
}
.table-dropdown { .table-dropdown {
.menu { .menu {
min-width: 100%; min-width: 100%;

View File

@ -148,6 +148,10 @@
background: transparent; background: transparent;
} }
.divider {
border-top: 0.05rem solid rgba($body-font-color-dark, 0.1);
}
.form-switch .form-icon::before { .form-switch .form-icon::before {
background: $bg-color-light-dark; background: $bg-color-light-dark;
} }

View File

@ -94,6 +94,10 @@
background: transparent; background: transparent;
} }
.divider {
border-top: 0.05rem solid rgba($body-font-color-dark, 0.1);
}
.tile { .tile {
transition: background 0.2s; transition: background 0.2s;

View File

@ -19,7 +19,7 @@ export const useConnectionsStore = defineStore('connections', {
state: () => ({ state: () => ({
connections: persistentStore.get('connections', []) as ConnectionParams[], connections: persistentStore.get('connections', []) as ConnectionParams[],
pinnedConnections: new Set([...persistentStore.get('pinnedConnections', []) as string[]]) as Set<string>, pinnedConnections: new Set([...persistentStore.get('pinnedConnections', []) as string[]]) as Set<string>,
lastConnections: persistentStore.get('lastConnections', {}) as {[k: string]: number} lastConnections: persistentStore.get('lastConnections', []) as {uid: string; time: number}[]
}), }),
getters: { getters: {
getConnectionName: state => (uid: string) => { getConnectionName: state => (uid: string) => {
@ -52,6 +52,8 @@ export const useConnectionsStore = defineStore('connections', {
deleteConnection (connection: ConnectionParams) { deleteConnection (connection: ConnectionParams) {
this.connections = (this.connections as ConnectionParams[]).filter(el => el.uid !== connection.uid); this.connections = (this.connections as ConnectionParams[]).filter(el => el.uid !== connection.uid);
persistentStore.set('connections', this.connections); persistentStore.set('connections', this.connections);
(this.pinnedConnections as Set<string>).delete(connection.uid);
persistentStore.set('pinnedConnections', [...this.pinnedConnections]);
}, },
editConnection (connection: ConnectionParams) { editConnection (connection: ConnectionParams) {
const editedConnections = (this.connections as ConnectionParams[]).map(conn => { const editedConnections = (this.connections as ConnectionParams[]).map(conn => {
@ -66,6 +68,10 @@ export const useConnectionsStore = defineStore('connections', {
this.connections = connections; this.connections = connections;
persistentStore.set('connections', this.connections); persistentStore.set('connections', this.connections);
}, },
updatePinnedConnections (pinned: string[]) {
this.pinnedConnections = new Set(pinned);
persistentStore.set('pinnedConnections', [...this.pinnedConnections]);
},
pinConnection (uid: string) { pinConnection (uid: string) {
(this.pinnedConnections as Set<string>).add(uid); (this.pinnedConnections as Set<string>).add(uid);
persistentStore.set('pinnedConnections', [...this.pinnedConnections]); persistentStore.set('pinnedConnections', [...this.pinnedConnections]);
@ -75,7 +81,13 @@ export const useConnectionsStore = defineStore('connections', {
persistentStore.set('pinnedConnections', [...this.pinnedConnections]); persistentStore.set('pinnedConnections', [...this.pinnedConnections]);
}, },
updateLastConnection (uid: string) { updateLastConnection (uid: string) {
this.lastConnections[uid] = new Date().getTime(); const cIndex = (this.lastConnections as {uid: string; time: number}[]).findIndex((c) => c.uid === uid);
if (cIndex >= 0)
this.lastConnections[cIndex].time = new Date().getTime();
else
this.lastConnections.push({ uid, time: new Date().getTime() });
persistentStore.set('lastConnections', this.lastConnections); persistentStore.set('lastConnections', this.lastConnections);
} }
} }

View File

@ -718,44 +718,6 @@ export const useWorkspacesStore = defineStore('workspaces', {
); );
persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs); persistentStore.set(uid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === uid).tabs);
}, },
// setTabFields ({ cUid, tUid, fields }: { cUid: string; tUid: string; fields: any }) {
// this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
// if (workspace.uid === cUid) {
// return {
// ...workspace,
// tabs: workspace.tabs.map(tab => {
// if (tab.uid === tUid)
// return { ...tab, fields };
// else
// return tab;
// })
// };
// }
// else
// return workspace;
// });
// persistentStore.set(cUid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === cUid).tabs);
// },
// setTabKeyUsage ({ cUid, tUid, keyUsage }: { cUid: string; tUid: string; keyUsage: any }) {
// this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
// if (workspace.uid === cUid) {
// return {
// ...workspace,
// tabs: workspace.tabs.map(tab => {
// if (tab.uid === tUid)
// return { ...tab, keyUsage };
// else
// return tab;
// })
// };
// }
// else
// return workspace;
// });
// persistentStore.set(cUid, (this.workspaces as Workspace[]).find(workspace => workspace.uid === cUid).tabs);
// },
setUnsavedChanges ({ uid, tUid, isChanged }: { uid: string; tUid: string; isChanged: boolean }) { setUnsavedChanges ({ uid, tUid, isChanged }: { uid: string; tUid: string; isChanged: boolean }) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => { this.workspaces = (this.workspaces as Workspace[]).map(workspace => {
if (workspace.uid === uid) { if (workspace.uid === uid) {