mirror of
https://github.com/Fabio286/antares.git
synced 2025-03-04 03:17:40 +01:00
feat(UI): folders customization
This commit is contained in:
parent
321b387083
commit
72accb7b0e
@ -1,3 +1,3 @@
|
||||
export function uidGen (prefix?: string) {
|
||||
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substr(2, 9).toUpperCase();
|
||||
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substring(2, 11).toUpperCase();
|
||||
}
|
||||
|
152
src/renderer/components/ModalFolderAppearence.vue
Normal file
152
src/renderer/components/ModalFolderAppearence.vue
Normal file
@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<Teleport to="#window-content">
|
||||
<div class="modal active">
|
||||
<a class="modal-overlay" @click.stop="closeModal" />
|
||||
<div ref="trapRef" class="modal-container p-0">
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-folder-edit mr-1" />
|
||||
<span class="cut-text">{{ t('message.editFolder') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
</div>
|
||||
<div class="modal-body pb-0">
|
||||
<div class="content">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group mb-4">
|
||||
<div class="col-3">
|
||||
<label class="form-label">{{ t('word.name') }}</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<input
|
||||
ref="firstInput"
|
||||
v-model="localFolder.name"
|
||||
class="form-input"
|
||||
type="text"
|
||||
required
|
||||
:placeholder="t('message.folderName')"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-3">
|
||||
<label class="form-label">{{ t('word.color') }}</label>
|
||||
</div>
|
||||
<div class="col-9 color-wrapper">
|
||||
<div
|
||||
v-for="color in colorPalette"
|
||||
:key="color.name"
|
||||
class="color-box"
|
||||
:title="color.name"
|
||||
:style="`background-color: ${color.hex}`"
|
||||
@click="localFolder.color = color.hex"
|
||||
>
|
||||
<i v-if="localFolder.color === color.hex" class="mdi mdi-check" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary mr-2" @click.stop="editFolderAppearence">
|
||||
{{ t('word.update') }}
|
||||
</button>
|
||||
<button class="btn btn-link" @click.stop="closeModal">
|
||||
{{ t('word.close') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, PropType, Ref, ref } from 'vue';
|
||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { unproxify } from '@/libs/unproxify';
|
||||
|
||||
const connectionsStore = useConnectionsStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
folder: {
|
||||
type: Object as PropType<SidebarElement>,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const { updateConnectionOrder } = connectionsStore;
|
||||
|
||||
const colorPalette = [
|
||||
{ name: 'default', hex: '#E36929' },
|
||||
{ name: 'grapefruit', hex: '#ED5565' },
|
||||
{ name: 'rose', hex: '#E3242B' },
|
||||
{ name: 'fire', hex: '#FDA50F' },
|
||||
{ name: 'sunflower', hex: '#FFCE54' },
|
||||
{ name: 'moss', hex: '#8A985E' },
|
||||
{ name: 'grass', hex: '#A0D468' },
|
||||
{ name: 'emerald', hex: '#038835' },
|
||||
{ name: 'mint', hex: '#48CFAD' },
|
||||
{ name: 'aqua', hex: '#4FC1E9' },
|
||||
{ name: 'royalblue', hex: '#4169E1' },
|
||||
{ name: 'bluejeans', hex: '#5D9CEC' },
|
||||
{ name: 'stone', hex: '#59788E' },
|
||||
{ name: 'lavander', hex: '#AC92EC' },
|
||||
{ name: 'pinkrose', hex: '#EC87C0' },
|
||||
{ name: 'smoke', hex: '#BEBDB8' },
|
||||
{ name: 'slate', hex: '#757C88' }
|
||||
];
|
||||
|
||||
const { trapRef } = useFocusTrap();
|
||||
|
||||
const firstInput: Ref<HTMLInputElement> = ref(null);
|
||||
const localFolder: Ref<SidebarElement> = ref(unproxify(props.folder));
|
||||
|
||||
const editFolderAppearence = () => {
|
||||
updateConnectionOrder(localFolder.value);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const closeModal = () => emit('close');
|
||||
|
||||
const onKey =(e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
closeModal();
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKey);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal-container {
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.color-wrapper{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 20px);
|
||||
gap: 5px;
|
||||
|
||||
.color-box {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -241,6 +241,7 @@ watch(() => dummyNested.value.length, () => {
|
||||
.folder-element {
|
||||
opacity: .6;
|
||||
height: 2.5rem;
|
||||
background: transparent;
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
@ -325,6 +326,8 @@ watch(() => dummyNested.value.length, () => {
|
||||
margin-bottom: 3px;
|
||||
position: relative;
|
||||
transition: opacity .2s;
|
||||
background: $bg-color-light-dark;
|
||||
border-radius: 8px;
|
||||
|
||||
&.ghost {
|
||||
margin:0 ;
|
||||
|
@ -17,6 +17,9 @@
|
||||
>
|
||||
<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.stop="showAppearenceModal">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-brush-variant text-light pr-1" /> {{ t('word.appearence') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showConfirmModal">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ t('word.delete') }}</span>
|
||||
</div>
|
||||
@ -37,6 +40,11 @@
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ModalFolderAppearence
|
||||
v-if="isFolderEdit"
|
||||
:folder="contextConnection"
|
||||
@close="hideAppearenceModal"
|
||||
/>
|
||||
</BaseContextMenu>
|
||||
</template>
|
||||
|
||||
@ -49,6 +57,7 @@ import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||
import ModalFolderAppearence from '@/components/ModalFolderAppearence.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -78,6 +87,8 @@ const props = defineProps({
|
||||
const emit = defineEmits(['close-context']);
|
||||
|
||||
const isConfirmModal = ref(false);
|
||||
const isFolderEdit = ref(false);
|
||||
const isConnectionEdit = ref(false);
|
||||
|
||||
const connectionName = computed(() => props.contextConnection.name || getConnectionName(props.contextConnection.uid) || t('word.folder', 1));
|
||||
const isConnected = computed(() => getWorkspace(props.contextConnection.uid)?.connectionStatus === 'connected');
|
||||
@ -101,6 +112,19 @@ const duplicateConnection = () => {
|
||||
closeContext();
|
||||
};
|
||||
|
||||
const showAppearenceModal = () => {
|
||||
if (props.contextConnection.isFolder)
|
||||
isFolderEdit.value = true;
|
||||
else
|
||||
isFolderEdit.value = true;
|
||||
};
|
||||
|
||||
const hideAppearenceModal = () => {
|
||||
isConnectionEdit.value = false;
|
||||
isFolderEdit.value = false;
|
||||
closeContext();
|
||||
};
|
||||
|
||||
const showConfirmModal = () => {
|
||||
isConfirmModal.value = true;
|
||||
};
|
||||
|
@ -78,6 +78,7 @@ import { useElementBounding } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
localStorage.setItem('opened-folders', '[]');
|
||||
|
||||
const applicationStore = useApplicationStore();
|
||||
const connectionsStore = useConnectionsStore();
|
||||
|
@ -30,7 +30,7 @@
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-refresh" />
|
||||
</div>
|
||||
<div v-if="isWindows" style="width: 140px;" />
|
||||
<div v-if="isWindows" :style="'width: 140px;'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -67,7 +67,7 @@ const windowTitle = computed(() => {
|
||||
|
||||
const connectionName = getConnectionName(selectedWorkspace.value);
|
||||
const workspace = getWorkspace(selectedWorkspace.value);
|
||||
const breadcrumbs = Object.values(workspace.breadcrumbs).filter(breadcrumb => breadcrumb) || [workspace.client];
|
||||
const breadcrumbs = workspace ? Object.values(workspace.breadcrumbs).filter(breadcrumb => breadcrumb) || [workspace.client] : [];
|
||||
|
||||
return [connectionName, ...breadcrumbs].join(' • ');
|
||||
});
|
||||
|
@ -144,7 +144,9 @@ export const enUS = {
|
||||
unpin: 'Unpin',
|
||||
console: 'Console',
|
||||
shortcuts: 'Shortcuts',
|
||||
folder: 'Folder | Folders'
|
||||
folder: 'Folder | Folders',
|
||||
appearence: 'Appearence',
|
||||
color: 'Color'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
@ -325,6 +327,8 @@ export const enUS = {
|
||||
nextResultsPage: 'Next results page',
|
||||
previousResultsPage: 'Previous results page',
|
||||
fillCell: 'Fill cell',
|
||||
editFolder: 'Edit folder',
|
||||
folderName: 'Folder name',
|
||||
deleteFolder: 'Delete folder'
|
||||
},
|
||||
faker: {
|
||||
|
@ -12,6 +12,7 @@ export interface SidebarElement {
|
||||
connections?: string[];
|
||||
color?: string;
|
||||
name?: string;
|
||||
icon?: null | string;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
@ -80,7 +81,8 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
this.connectionsOrder.push({
|
||||
isFolder: false,
|
||||
uid: connection.uid,
|
||||
client: connection.client
|
||||
client: connection.client,
|
||||
icon: null
|
||||
});
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
},
|
||||
@ -90,7 +92,7 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
isFolder: true,
|
||||
uid: uidGen('F'),
|
||||
name: '',
|
||||
color: '#e36929',
|
||||
color: '#E36929',
|
||||
connections: params.connections
|
||||
});
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
@ -109,6 +111,22 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
persistentStore.set('connections', this.connections);
|
||||
|
||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).filter(el => el.uid !== connection.uid);
|
||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).map(el => { // Removes connection from folders
|
||||
if (el.isFolder && el.connections.includes(connection.uid))
|
||||
el.connections = el.connections.filter(uid => uid !== connection.uid);
|
||||
|
||||
return el;
|
||||
});
|
||||
|
||||
// Clear empty folders
|
||||
const emptyFolders = (this.connectionsOrder as SidebarElement[]).reduce<string[]>((acc, curr) => {
|
||||
if (curr.connections && curr.connections.length === 0)
|
||||
acc.push(curr.uid);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).filter(el => !emptyFolders.includes(el.uid));
|
||||
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
},
|
||||
editConnection (connection: ConnectionParams) {
|
||||
@ -117,7 +135,6 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
return conn;
|
||||
});
|
||||
this.connections = editedConnections;
|
||||
this.selected_conection = {};
|
||||
persistentStore.set('connections', this.connections);
|
||||
},
|
||||
updateConnections (connections: ConnectionParams[]) {
|
||||
@ -162,6 +179,13 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
this.connectionsOrder = connections;
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
},
|
||||
updateConnectionOrder (element: SidebarElement) {
|
||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).map(el => {
|
||||
if (el.uid === element.uid)
|
||||
el = element;
|
||||
return el;
|
||||
});
|
||||
},
|
||||
updateLastConnection (uid: string) {
|
||||
const cIndex = (this.lastConnections as {uid: string; time: number}[]).findIndex((c) => c.uid === uid);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user