mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
refactor: changes to implement folders
This commit is contained in:
@ -55,20 +55,7 @@
|
||||
<div class="panel-subtitle">
|
||||
{{ clients.get(connection.client) || connection.client }}
|
||||
</div>
|
||||
<div class="all-connections-buttons p-absolute d-flex" style="top: 0; right: 0;">
|
||||
<i
|
||||
v-if="connection.isPinned"
|
||||
class="all-connections-pinned mdi mdi-18px"
|
||||
:class="connectionHover === connection.uid ? 'mdi-pin-off' : 'mdi-pin'"
|
||||
:title="t('word.unpin')"
|
||||
@click.stop="unpinConnection(connection.uid)"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="all-connections-pin mdi mdi-18px mdi-pin mdi-rotate-45"
|
||||
:title="t('word.pin')"
|
||||
@click.stop="pinConnection(connection.uid)"
|
||||
/>
|
||||
<div class="all-connections-buttons p-absolute d-flex" :style="'top: 0; right: 0;'">
|
||||
<i
|
||||
class="all-connections-delete mdi mdi-delete mdi-18px ml-2"
|
||||
:title="t('word.delete')"
|
||||
@ -126,7 +113,7 @@
|
||||
key="trick"
|
||||
readonly
|
||||
class="p-absolute"
|
||||
style="width: 1px; height: 1px; opacity: 0;"
|
||||
:style="'width: 1px; height: 1px; opacity: 0;'"
|
||||
type="text"
|
||||
>
|
||||
<!-- workaround for useFocusTrap $lastFocusable -->
|
||||
@ -171,15 +158,12 @@ const connectionsStore = useConnectionsStore();
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
|
||||
const { connections,
|
||||
pinnedConnections,
|
||||
lastConnections
|
||||
} = storeToRefs(connectionsStore);
|
||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||
|
||||
const {
|
||||
getConnectionName,
|
||||
pinConnection,
|
||||
unpinConnection,
|
||||
deleteConnection
|
||||
} = connectionsStore;
|
||||
const { selectWorkspace } = workspacesStore;
|
||||
@ -206,13 +190,10 @@ const sortedConnections = computed(() => {
|
||||
const connTime = lastConnections.value.find((lc) => lc.uid === c.uid)?.time || 0;
|
||||
return {
|
||||
...c,
|
||||
time: connTime,
|
||||
isPinned: pinnedConnections.value.has(c.uid)
|
||||
time: connTime
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.isPinned < b.isPinned) return 1;
|
||||
if (a.isPinned > b.isPinned) return -1;
|
||||
if (a.time < b.time) return 1;
|
||||
if (a.time > b.time) return -1;
|
||||
return 0;
|
||||
|
173
src/renderer/components/SettingBarConnections.vue
Normal file
173
src/renderer/components/SettingBarConnections.vue
Normal file
@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<Draggable
|
||||
:list="localList"
|
||||
item-key="'uid'"
|
||||
ghost-class="ghost"
|
||||
:group="{ name: 'connections', pull: 'clone' }"
|
||||
class="pb-1"
|
||||
:swap-threshold="0.3"
|
||||
@start="emit('start', $event)"
|
||||
@end="emit('end', $event)"
|
||||
@move="emit('move', $event)"
|
||||
@change="emit('update:modelValue', localList)"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<li
|
||||
v-if="element.isFolder || !folderedConnections.includes(element.uid)"
|
||||
:draggable="true"
|
||||
class="settingbar-element btn btn-link"
|
||||
:class="{ 'selected': element.uid === selectedWorkspace }"
|
||||
@dragstart="draggedElement = element.uid"
|
||||
@dragend="coveredElement = false"
|
||||
@contextmenu.prevent="emit('context', $event, element)"
|
||||
>
|
||||
<div
|
||||
v-if="!element.isFolder && !folderedConnections.includes(element.uid)"
|
||||
class="p-relative"
|
||||
:style="`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`"
|
||||
:title="getConnectionName(element.uid)"
|
||||
@click.stop="selectWorkspace(element.uid)"
|
||||
>
|
||||
<!-- Creates a new folder -->
|
||||
<SettingBarConnections
|
||||
v-if="draggedElement && !foldersUid.includes(draggedElement)"
|
||||
class="drag-area"
|
||||
:class="[{'folder-preview': coveredElement === element.uid && draggedElement !== coveredElement}]"
|
||||
:list="dummyNested"
|
||||
@dragenter="coveredElement = element.uid"
|
||||
@dragleave="coveredElement = false"
|
||||
@change="createFolder"
|
||||
/>
|
||||
<i
|
||||
class="settingbar-element-icon dbi"
|
||||
:class="[`dbi-${element.client}`, getStatusBadge(element.uid)]"
|
||||
/>
|
||||
<small class="settingbar-element-name">{{ getConnectionName(element.uid) }}</small>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="element.isFolder"
|
||||
class="p-relative"
|
||||
:style="`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`"
|
||||
>
|
||||
<i class="settingbar-element-icon mdi mdi-folder mdi-36px" />
|
||||
<small class="settingbar-element-name">{{ element.name }}</small>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</Draggable>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType, Ref, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import * as Draggable from 'vuedraggable';
|
||||
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
const connectionsStore = useConnectionsStore();
|
||||
|
||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||
const { getFolders: folders } = storeToRefs(connectionsStore);
|
||||
|
||||
const { getWorkspace, selectWorkspace } = workspacesStore;
|
||||
const { getConnectionName, addFolder } = connectionsStore;
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<SidebarElement[]>,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['start', 'end', 'move', 'context', 'update:modelValue']);
|
||||
|
||||
const localList = ref(props.modelValue);
|
||||
const dummyNested = ref([]);
|
||||
const draggedElement: Ref<string | false> = ref(false);
|
||||
const coveredElement: Ref<string | false> = ref(false);
|
||||
|
||||
const foldersUid = computed(() => folders.value.reduce<string[]>((acc, curr) => {
|
||||
acc.push(curr.uid);
|
||||
return acc;
|
||||
}, []));
|
||||
const folderedConnections = computed(() => {
|
||||
return folders.value.reduce<string[]>((acc, curr) => {
|
||||
acc = [...acc, ...curr.connections];
|
||||
return acc;
|
||||
}, []);
|
||||
});
|
||||
|
||||
const createFolder = ({ added }: {added: { element: SidebarElement }}) => {
|
||||
if (typeof coveredElement.value === 'string' && !added.element.isFolder) {
|
||||
console.log('added', added.element);
|
||||
// Create folder
|
||||
addFolder({
|
||||
after: coveredElement.value,
|
||||
connections: [coveredElement.value, added.element.uid]
|
||||
});
|
||||
|
||||
coveredElement.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (uid: string) => {
|
||||
if (getWorkspace(uid)) {
|
||||
const status = getWorkspace(uid).connectionStatus;
|
||||
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return 'badge badge-connected';
|
||||
case 'connecting':
|
||||
return 'badge badge-connecting';
|
||||
case 'failed':
|
||||
return 'badge badge-failed';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
watch(() => dummyNested.value.length, () => {
|
||||
dummyNested.value = [];
|
||||
});
|
||||
|
||||
watch(() => props.modelValue, (value) => {
|
||||
localList.value = value;
|
||||
});
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.drag-area {
|
||||
background-color: transparent;
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
transition: all .2s;
|
||||
|
||||
&.folder-preview {
|
||||
border: 1px dashed;
|
||||
border-radius: 5px;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
li {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -3,20 +3,6 @@
|
||||
:context-event="contextEvent"
|
||||
@close-context="$emit('close-context')"
|
||||
>
|
||||
<div
|
||||
v-if="isPinned"
|
||||
class="context-element"
|
||||
@click="unpin"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pin-off text-light pr-1" /> {{ t('word.unpin') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="context-element"
|
||||
@click="pin"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pin mdi-rotate-45 text-light pr-1" /> {{ t('word.pin') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="isConnected"
|
||||
class="context-element"
|
||||
@ -24,7 +10,11 @@
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-power text-light pr-1" /> {{ t('word.disconnect') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="duplicateConnection">
|
||||
<div
|
||||
v-if="!contextConnection.isFolder"
|
||||
class="context-element"
|
||||
@click="duplicateConnection"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ t('word.duplicate') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showConfirmModal">
|
||||
@ -38,7 +28,7 @@
|
||||
>
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ t('message.deleteConnection') }}
|
||||
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ t(contextConnection.isFolder ? 'message.deleteFolder' : 'message.deleteConnection') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
@ -55,26 +45,22 @@ import { computed, Prop, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useConnectionsStore } from '@/stores/connections';
|
||||
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||
import { ConnectionParams } from 'common/interfaces/antares';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const connectionsStore = useConnectionsStore();
|
||||
|
||||
const {
|
||||
getConnectionByUid,
|
||||
getConnectionName,
|
||||
addConnection,
|
||||
deleteConnection,
|
||||
pinConnection,
|
||||
unpinConnection
|
||||
deleteConnection
|
||||
} = connectionsStore;
|
||||
|
||||
const { pinnedConnections } = storeToRefs(connectionsStore);
|
||||
|
||||
const workspacesStore = useWorkspacesStore();
|
||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||
|
||||
@ -86,16 +72,15 @@ const {
|
||||
|
||||
const props = defineProps({
|
||||
contextEvent: MouseEvent,
|
||||
contextConnection: Object as Prop<ConnectionParams>
|
||||
contextConnection: Object as Prop<SidebarElement>
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close-context']);
|
||||
|
||||
const isConfirmModal = ref(false);
|
||||
|
||||
const connectionName = computed(() => getConnectionName(props.contextConnection.uid));
|
||||
const isConnected = computed(() => getWorkspace(props.contextConnection.uid).connectionStatus === 'connected');
|
||||
const isPinned = computed(() => pinnedConnections.value.has(props.contextConnection.uid));
|
||||
const connectionName = computed(() => props.contextConnection.name || getConnectionName(props.contextConnection.uid) || t('word.folder', 1));
|
||||
const isConnected = computed(() => getWorkspace(props.contextConnection.uid)?.connectionStatus === 'connected');
|
||||
|
||||
const confirmDeleteConnection = () => {
|
||||
if (selectedWorkspace.value === props.contextConnection.uid)
|
||||
@ -105,7 +90,7 @@ const confirmDeleteConnection = () => {
|
||||
};
|
||||
|
||||
const duplicateConnection = () => {
|
||||
let connectionCopy = Object.assign({}, props.contextConnection);
|
||||
let connectionCopy = getConnectionByUid(props.contextConnection.uid);
|
||||
connectionCopy = {
|
||||
...connectionCopy,
|
||||
uid: uidGen('C'),
|
||||
@ -125,16 +110,6 @@ const hideConfirmModal = () => {
|
||||
closeContext();
|
||||
};
|
||||
|
||||
const pin = () => {
|
||||
pinConnection(props.contextConnection.uid);
|
||||
closeContext();
|
||||
};
|
||||
|
||||
const unpin = () => {
|
||||
unpinConnection(props.contextConnection.uid);
|
||||
closeContext();
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
disconnectWorkspace(props.contextConnection.uid);
|
||||
closeContext();
|
||||
|
@ -8,47 +8,10 @@
|
||||
@close-context="isContext = false"
|
||||
/>
|
||||
<ul class="settingbar-elements">
|
||||
<Draggable
|
||||
v-model="pinnedConnectionsArr"
|
||||
:item-key="'uid'"
|
||||
@start="isDragging = true"
|
||||
@end="dragStop"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<li
|
||||
:draggable="true"
|
||||
class="settingbar-element btn btn-link"
|
||||
:class="{ 'selected': element.uid === selectedWorkspace }"
|
||||
:title="getConnectionName(element.uid)"
|
||||
@click.stop="selectWorkspace(element.uid)"
|
||||
@contextmenu.prevent="contextMenu($event, element)"
|
||||
>
|
||||
<i
|
||||
class="settingbar-element-icon dbi"
|
||||
:class="[`dbi-${element.client}`, getStatusBadge(element.uid), (pinnedConnections.has(element.uid) ? 'settingbar-element-pin' : false)]"
|
||||
/>
|
||||
<small class="settingbar-element-name">{{ getConnectionName(element.uid) }}</small>
|
||||
</li>
|
||||
</template>
|
||||
</Draggable>
|
||||
|
||||
<div v-if="pinnedConnectionsArr.length" class="divider" />
|
||||
|
||||
<li
|
||||
v-for="connection in unpinnedConnectionsArr"
|
||||
:key="connection.uid"
|
||||
class="settingbar-element btn btn-link"
|
||||
:class="{ 'selected': connection.uid === selectedWorkspace }"
|
||||
:title="getConnectionName(connection.uid)"
|
||||
@click.stop="selectWorkspace(connection.uid)"
|
||||
@contextmenu.prevent="contextMenu($event, connection)"
|
||||
>
|
||||
<i
|
||||
class="settingbar-element-icon dbi"
|
||||
:class="[`dbi-${connection.client}`, getStatusBadge(connection.uid)]"
|
||||
/>
|
||||
<small class="settingbar-element-name">{{ getConnectionName(connection.uid) }}</small>
|
||||
</li>
|
||||
<SettingBarConnections
|
||||
v-model="connectionsArr"
|
||||
@context="contextMenu"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -102,12 +65,11 @@
|
||||
import { ref, Ref, computed, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useApplicationStore } from '@/stores/application';
|
||||
import { useConnectionsStore } from '@/stores/connections';
|
||||
import { useConnectionsStore, SidebarElement } from '@/stores/connections';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import * as Draggable from 'vuedraggable';
|
||||
import SettingBarContext from '@/components/SettingBarContext.vue';
|
||||
import { ConnectionParams } from 'common/interfaces/antares';
|
||||
import SettingBarConnections from '@/components/SettingBarConnections.vue';
|
||||
import { useElementBounding } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@ -119,121 +81,42 @@ const workspacesStore = useWorkspacesStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const { updateStatus } = storeToRefs(applicationStore);
|
||||
const { connections: storedConnections, pinnedConnections, lastConnections } = storeToRefs(connectionsStore);
|
||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||
const { getConnectionsOrder: connectionsOrder } = storeToRefs(connectionsStore);
|
||||
const { disableScratchpad } = storeToRefs(settingsStore);
|
||||
|
||||
const { showSettingModal, showScratchpad } = applicationStore;
|
||||
const { getConnectionName, updatePinnedConnections } = connectionsStore;
|
||||
const { getWorkspace, selectWorkspace } = workspacesStore;
|
||||
const { updateConnectionsOrder } = connectionsStore;
|
||||
const { selectWorkspace } = workspacesStore;
|
||||
|
||||
const emit = defineEmits(['show-connections-modal']);
|
||||
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
const sidebarConnections: Ref<HTMLDivElement> = ref(null);
|
||||
const isContext: Ref<boolean> = ref(false);
|
||||
const isDragging: Ref<boolean> = ref(false);
|
||||
const isScrollable: Ref<boolean> = ref(false);
|
||||
const contextEvent: Ref<MouseEvent> = ref(null);
|
||||
const contextConnection: Ref<ConnectionParams> = ref(null);
|
||||
const contextConnection: Ref<SidebarElement> = ref(null);
|
||||
const sidebarConnectionsHeight = ref(useElementBounding(sidebarConnections)?.height);
|
||||
|
||||
const pinnedConnectionsArr = computed({
|
||||
get: () => [...pinnedConnections.value].map(c => storedConnections.value.find(sc => sc.uid === c)).filter(Boolean),
|
||||
set: (value: ConnectionParams[]) => {
|
||||
const pinnedUid = value.reduce((acc, curr) => {
|
||||
acc.push(curr.uid);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
updatePinnedConnections(pinnedUid);
|
||||
const connectionsArr = computed({
|
||||
get: () => connectionsOrder.value,
|
||||
set: (value: SidebarElement[]) => {
|
||||
updateConnectionsOrder(value);
|
||||
}
|
||||
});
|
||||
|
||||
const unpinnedConnectionsArr = computed(() => {
|
||||
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 contextMenu = (event: MouseEvent, connection: ConnectionParams) => {
|
||||
const contextMenu = (event: MouseEvent, connection: SidebarElement) => {
|
||||
contextEvent.value = event;
|
||||
contextConnection.value = connection;
|
||||
isContext.value = true;
|
||||
};
|
||||
|
||||
const tooltipPosition = (e: Event) => {
|
||||
const el = (e.target ? e.target : e) as unknown as HTMLElement;
|
||||
const tooltip = el.querySelector<HTMLElement>('.ex-tooltip-content');
|
||||
if (tooltip) {
|
||||
const fromTop = isLinux
|
||||
? window.scrollY + el.getBoundingClientRect().top + (el.offsetHeight / 4)
|
||||
: window.scrollY + el.getBoundingClientRect().top - (el.offsetHeight / 4);
|
||||
tooltip.style.top = `${fromTop}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (uid: string) => {
|
||||
if (getWorkspace(uid)) {
|
||||
const status = getWorkspace(uid).connectionStatus;
|
||||
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return 'badge badge-connected';
|
||||
case 'connecting':
|
||||
return 'badge badge-connecting';
|
||||
case 'failed':
|
||||
return 'badge badge-failed';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dragStop = (e: any) => {
|
||||
isDragging.value = false;
|
||||
|
||||
setTimeout(() => {
|
||||
tooltipPosition(e.originalEvent.target.parentNode);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
watch(sidebarConnectionsHeight, (value) => {
|
||||
isScrollable.value = value < sidebarConnections.value.scrollHeight;
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
watch(selectedWorkspace, (newVal, oldVal) => {
|
||||
if (newVal !== oldVal) {
|
||||
setTimeout(() => {
|
||||
|
Reference in New Issue
Block a user