mirror of https://github.com/Fabio286/antares.git
484 lines
12 KiB
Vue
484 lines
12 KiB
Vue
<template>
|
|
<div
|
|
v-tooltip="{
|
|
strategy: 'fixed',
|
|
placement: 'right',
|
|
content: folder.name,
|
|
disabled: isOpen || !folder.name
|
|
}"
|
|
class="settingbar-element folder btn btn-link p-1"
|
|
:class="[{ 'selected-inside': hasSelectedInside && !isOpen }]"
|
|
:style="isOpen ? `height: auto; opacity: 1;` : null"
|
|
>
|
|
<Draggable
|
|
class="folder-container"
|
|
:item-key="((item: string) => localList.indexOf(item))"
|
|
:class="[{'opened': isOpen}]"
|
|
:style="[
|
|
`background: ${folder.color};`,
|
|
isOpen ? `max-height: ${60*(folder.connections.length+1)}px` : 'max-height: 60px',
|
|
!isOpen || folderDrag ? `overflow: hidden;` : ''
|
|
]"
|
|
:list="localList"
|
|
ghost-class="ghost"
|
|
:group="{ name: 'connections', put: folderDrag ? undefined : 'clone' }"
|
|
@start="dragStart"
|
|
@end="dragStop"
|
|
>
|
|
<template #header>
|
|
<div
|
|
v-if="!isOpen"
|
|
class="folder-overlay"
|
|
@click="openFolder"
|
|
@contextmenu.stop="emit('context', {event: $event, content: folder})"
|
|
/>
|
|
<div
|
|
v-if="isOpen"
|
|
class="folder-icon"
|
|
:style="`color: ${folder.color};`"
|
|
@click="closeFolder"
|
|
>
|
|
<BaseIcon
|
|
class="folder-icon-open"
|
|
icon-name="mdiFolderOpen"
|
|
:size="36"
|
|
/>
|
|
<BaseIcon
|
|
class="folder-icon-close"
|
|
icon-name="mdiFolder"
|
|
:size="36"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template #item="{ element }">
|
|
<div
|
|
:key="element"
|
|
v-tooltip="{
|
|
strategy: 'fixed',
|
|
placement: 'right',
|
|
content: getConnectionName(element)
|
|
}"
|
|
class="folder-element"
|
|
:class="{ 'selected': element === selectedWorkspace }"
|
|
@click="emit('select-workspace', element)"
|
|
@contextmenu.stop="emit('context', {event: $event, content: getConnectionOrderByUid(element)})"
|
|
>
|
|
<div
|
|
v-if="getConnectionOrderByUid(element).icon"
|
|
class="folder-element-icon"
|
|
:class="[getStatusBadge(element)]"
|
|
>
|
|
<BaseIcon
|
|
:icon-name="camelize(getConnectionOrderByUid(element).icon)"
|
|
:size="36"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="folder-element-icon dbi"
|
|
:class="[`dbi-${getConnectionOrderByUid(element).client}`, getStatusBadge(element)]"
|
|
/>
|
|
<small v-if="isOpen" class="folder-element-name">{{ getConnectionOrderByUid(element)?.name || getConnectionName(element) }}</small>
|
|
</div>
|
|
</template>
|
|
</Draggable>
|
|
<SettingBarConnections
|
|
v-if="draggedElement && !foldersUid.includes(draggedElement)"
|
|
class="drag-area"
|
|
:class="[{'folder-preview': coveredElement === folder.uid && draggedElement !== coveredElement}]"
|
|
:list="dummyNested"
|
|
:swap-threshold="1"
|
|
@dragenter="emit('covered')"
|
|
@dragleave="emit('uncovered')"
|
|
@change="addIntoFolder"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { storeToRefs } from 'pinia';
|
|
import { computed, PropType, ref, watch } from 'vue';
|
|
import * as Draggable from 'vuedraggable';
|
|
|
|
import BaseIcon from '@/components/BaseIcon.vue';
|
|
import SettingBarConnections from '@/components/SettingBarConnections.vue';
|
|
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
|
|
|
const workspacesStore = useWorkspacesStore();
|
|
const connectionsStore = useConnectionsStore();
|
|
|
|
const { getFolders: folders } = storeToRefs(connectionsStore);
|
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
|
|
const { getWorkspace } = workspacesStore;
|
|
const { getConnectionOrderByUid, getConnectionName, addToFolder } = connectionsStore;
|
|
|
|
const foldersOpened = JSON.parse(localStorage.getItem('opened-folders')) || [];
|
|
|
|
const props = defineProps({
|
|
folder: {
|
|
type: Object as PropType<SidebarElement>,
|
|
required: true
|
|
},
|
|
folderDrag: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
draggedElement: {
|
|
type: [String, Boolean] as PropType<string | false>,
|
|
required: true
|
|
},
|
|
coveredElement: {
|
|
type: [String, Boolean] as PropType<string | false>,
|
|
required: true
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['context', 'covered', 'uncovered', 'select-workspace', 'folder-sort', 'folder-drag']);
|
|
|
|
const isOpen = ref(foldersOpened.includes(props.folder.uid));
|
|
const localList = ref(props.folder.connections);
|
|
const dummyNested = ref([]);
|
|
|
|
const hasSelectedInside = computed(() => localList.value.includes(selectedWorkspace.value));
|
|
|
|
const foldersUid = computed(() => folders.value.reduce<string[]>((acc, curr) => {
|
|
acc.push(curr.uid);
|
|
return acc;
|
|
}, []));
|
|
|
|
const addIntoFolder = ({ added }: {added: { element: SidebarElement }}) => {
|
|
if (typeof props.coveredElement === 'string' && !added.element.isFolder) {
|
|
addToFolder({
|
|
folder: props.coveredElement,
|
|
connection: added.element.uid
|
|
});
|
|
|
|
emit('uncovered');
|
|
}
|
|
};
|
|
|
|
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 '';
|
|
}
|
|
}
|
|
};
|
|
|
|
const openFolder = () => {
|
|
isOpen.value = true;
|
|
const opened: string[] = JSON.parse(localStorage.getItem('opened-folders')) || [];
|
|
opened.push(props.folder.uid);
|
|
localStorage.setItem('opened-folders', JSON.stringify(opened));
|
|
};
|
|
|
|
const closeFolder = () => {
|
|
isOpen.value = false;
|
|
let opened: string[] = JSON.parse(localStorage.getItem('opened-folders')) || [];
|
|
opened = opened.filter(uid => uid !== props.folder.uid);
|
|
localStorage.setItem('opened-folders', JSON.stringify(opened));
|
|
};
|
|
|
|
const dragStart = () => {
|
|
emit('folder-drag', true);
|
|
};
|
|
|
|
const dragStop = () => {
|
|
emit('folder-drag', false);
|
|
};
|
|
|
|
const camelize = (text: string) => {
|
|
const textArr = text.split('-');
|
|
for (let i = 0; i < textArr.length; i++) {
|
|
if (i === 0) continue;
|
|
textArr[i] = textArr[i].charAt(0).toUpperCase() + textArr[i].slice(1);
|
|
}
|
|
|
|
return textArr.join('');
|
|
};
|
|
|
|
watch(() => dummyNested.value.length, () => {
|
|
dummyNested.value = [];
|
|
});
|
|
|
|
emit('folder-sort');// To apply changes on component key change
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.folder {
|
|
position: relative;
|
|
&.selected-inside {
|
|
opacity: 1!important;
|
|
|
|
&::after {
|
|
height: 2.5rem;
|
|
position: absolute;
|
|
}
|
|
}
|
|
|
|
&::after {
|
|
content: "";
|
|
height: 0;
|
|
width: 3px;
|
|
transition: height 0.2s;
|
|
background-color: rgba($color: #fff, $alpha: 0.8);
|
|
border-radius: $border-radius;
|
|
position: absolute;
|
|
left: 0;
|
|
}
|
|
}
|
|
|
|
.folder-container {
|
|
display: grid;
|
|
grid-template-columns: auto auto;
|
|
grid-template-rows: 50%;
|
|
gap: 4px;
|
|
padding: 4px;
|
|
height: 100%;
|
|
width: 100%;
|
|
border-radius: 15px;
|
|
transition: background .3s;
|
|
color: $body-font-color-dark;
|
|
|
|
&::before {
|
|
content: "";
|
|
height: 0;
|
|
width: 3px;
|
|
transition: height 0.2s;
|
|
background-color: rgba($color: #fff, $alpha: 0.8);
|
|
border-radius: $border-radius;
|
|
position: absolute;
|
|
left: -11px;
|
|
}
|
|
|
|
.folder-element-icon {
|
|
margin: 0 auto;
|
|
|
|
&.badge::after {
|
|
top: 10px;
|
|
right: 0px;
|
|
position: absolute;
|
|
display: none;
|
|
}
|
|
|
|
&.badge-update::after {
|
|
bottom: initial;
|
|
}
|
|
}
|
|
|
|
&.opened {
|
|
gap: 4px 6px;
|
|
grid-template-columns: auto;
|
|
grid-template-rows: auto;
|
|
background: rgba($color: #fff, $alpha: 0.1)!important;
|
|
transition: max-height .1s;
|
|
|
|
.folder-element {
|
|
opacity: .6;
|
|
height: 2.5rem;
|
|
max-width: initial;
|
|
max-height: initial;
|
|
background: transparent;
|
|
|
|
&.ghost {
|
|
background: $bg-color-light-dark;
|
|
&.selected::before {
|
|
height: 0;
|
|
position: absolute;
|
|
}
|
|
}
|
|
|
|
&.selected {
|
|
opacity: 1;
|
|
|
|
&::before {
|
|
height: 2.5rem;
|
|
position: absolute;
|
|
}
|
|
}
|
|
|
|
&::before {
|
|
content: "";
|
|
height: 0;
|
|
width: 3px;
|
|
transition: height 0.2s;
|
|
background-color: rgba($color: #fff, $alpha: 0.8);
|
|
border-radius: $border-radius;
|
|
position: absolute;
|
|
left: -11px;
|
|
}
|
|
|
|
.folder-element-icon {
|
|
margin: 0 auto;
|
|
font-size: 36px;
|
|
display: flex;
|
|
|
|
&.badge::after {
|
|
top: 14px;
|
|
right: -4px;
|
|
position: absolute;
|
|
display: block;
|
|
}
|
|
|
|
&.badge-update::after {
|
|
bottom: initial;
|
|
}
|
|
}
|
|
}
|
|
|
|
.folder-icon {
|
|
height: 2.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 3px;
|
|
position: relative;
|
|
transition: opacity .2s;
|
|
|
|
.folder-icon-open {
|
|
display: block;
|
|
}
|
|
|
|
.folder-icon-close {
|
|
display: none;
|
|
}
|
|
|
|
&:hover {
|
|
opacity: 1;
|
|
|
|
.folder-icon-open {
|
|
display: none;
|
|
}
|
|
|
|
.folder-icon-close {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.folder-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.folder-element {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: center;
|
|
max-width: 23px;
|
|
max-height: 23px;
|
|
margin-bottom: 3px;
|
|
position: relative;
|
|
transition: opacity .2s;
|
|
background: $bg-color-light-dark;
|
|
border-radius: 8px;
|
|
|
|
&.ghost {
|
|
margin:0 ;
|
|
margin-bottom: 3px;
|
|
height: 2.5rem;
|
|
}
|
|
|
|
&:hover, &.selected {
|
|
opacity: 1;
|
|
}
|
|
|
|
.folder-element-icon {
|
|
transition: margin .2s;
|
|
}
|
|
|
|
.folder-element-name {
|
|
position: absolute;
|
|
max-width: 90%;
|
|
bottom: 0;
|
|
font-size: 65%;
|
|
font-style: normal;
|
|
display: block;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
line-height: 1.02;
|
|
transition: bottom .2s;
|
|
}
|
|
}
|
|
|
|
&:not(.opened){
|
|
.folder-element {
|
|
|
|
.folder-element-icon,
|
|
.folder-element-icon svg {
|
|
width: 21px;
|
|
height: 21px;
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.ghost {
|
|
border-radius: 15px!important;
|
|
background: rgba($color: #fff, $alpha: 0.1);
|
|
|
|
&.folder-element {
|
|
height: $settingbar-width;
|
|
border-radius: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 4px;
|
|
position: relative;
|
|
transition: opacity .2s;
|
|
|
|
&:hover, &.selected {
|
|
opacity: 1;
|
|
}
|
|
|
|
.folder-element-icon {
|
|
margin: 0 auto;
|
|
font-size: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
&.badge::after {
|
|
top: 5px;
|
|
right: -4px;
|
|
position: absolute;
|
|
}
|
|
|
|
&.badge-update::after {
|
|
bottom: initial;
|
|
}
|
|
}
|
|
|
|
.folder-element-name {
|
|
position: absolute;
|
|
max-width: 90%;
|
|
bottom: 5px;
|
|
text-align: center;
|
|
font-size: 65%;
|
|
font-style: normal;
|
|
display: block;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
line-height: 1.02;
|
|
}
|
|
}
|
|
}
|
|
</style>
|