1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-02-10 00:30:48 +01:00

feat: search form in all connections modal

This commit is contained in:
Fabio Di Stasio 2022-07-04 12:27:04 +02:00
parent a703dcc53e
commit ec5ab73b19
7 changed files with 165 additions and 84 deletions

View File

@ -14,60 +14,89 @@
</div> </div>
<div class="modal-body py-0"> <div class="modal-body py-0">
<div class="columns"> <div class="columns">
<div <div class="connections-search column col-12 columns col-gapless">
v-for="connection in connections" <div class="column col-12 mt-2">
:key="connection.uid" <div
class="column col-md-6 col-lg-4 col-3 p-3" ref="searchForm"
> class="form-group has-icon-right p-2 m-0"
<div class="panel"> >
<div class="panel-header text-center"> <input
<figure class="avatar avatar-lg"> v-model="searchTerm"
<i class="settingbar-element-icon dbi" :class="[`dbi-${connection.client}`]" /> class="form-input"
</figure> type="text"
<div class="panel-title h6 mt-10"> :placeholder="t('message.searchForConnections')"
{{ getConnectionName(connection.uid) }} @keypress.esc="searchTerm = ''"
</div> >
<div class="panel-subtitle"> <i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
{{ connection.client }} <i
</div> v-else
</div> class="form-icon c-hand mdi mdi-backspace mdi-18px pr-4"
<div class="panel-body text-center"> @click="searchTerm = ''"
<div v-if="connection.databasePath"> />
<div class="pl-1 text-break">
<span class="text-bold">PATH:</span> {{ connection.databasePath }}
</div>
</div>
<div v-else>
<div class="pl-1 text-break">
<span class="text-bold">HOST:</span> {{ connection.host }}
</div>
</div>
<div v-if="connection.user">
<div class="pl-1 text-break">
<span class="text-bold">USER:</span> {{ connection.user }}
</div>
</div>
<div v-if="connection.schema">
<div class="pl-1 text-break">
<span class="text-bold">SCHEMA:</span> {{ connection.schema }}
</div>
</div>
<div v-if="connection.database">
<div class="pl-1 text-break">
<span class="text-bold">DATABASE:</span> {{ connection.database }}
</div>
</div>
</div>
<div class="panel-footer text-center py-0">
<div v-if="connection.ssl" class="chip bg-success mt-2">
SSL
</div>
<div v-if="connection.ssh" class="chip bg-success mt-2">
SSH
</div>
</div> </div>
</div> </div>
</div> </div>
<TransitionGroup name="fade" :duration="{enter: 200, leave: 200}">
<div
v-for="connection in filteredConnections"
:key="connection.uid"
class="connection-block column col-md-6 col-lg-4 col-3 p-3"
tabindex="0"
@click.stop="selectConnection(connection.uid)"
@keypress.stop.enter="selectConnection(connection.uid)"
>
<div class="panel">
<div class="panel-header p-2 text-center">
<figure class="avatar avatar-lg pt-1 mb-1">
<i class="settingbar-element-icon dbi" :class="[`dbi-${connection.client}`]" />
</figure>
<div class="panel-title h6 text-ellipsis">
{{ getConnectionName(connection.uid) }}
</div>
<div class="panel-subtitle">
{{ clients.get(connection.client) || connection.client }}
</div>
</div>
<div class="panel-body text-center">
<div v-if="connection.databasePath">
<div class="text-ellipsis" :title="connection.databasePath">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{ connection.databasePath }}</span>
</div>
</div>
<div v-else>
<div class="text-ellipsis" :title="`${connection.host}:${connection.port}`">
<i class="mdi mdi-server d-inline" /> <span class="text-bold">{{ connection.host }}:{{ connection.port }}</span>
</div>
</div>
<div v-if="connection.user">
<div class="text-ellipsis">
<i class="mdi mdi-account d-inline" /> <span class="text-bold">{{ connection.user }}</span>
</div>
</div>
<div v-if="connection.schema">
<div class="text-ellipsis">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{ connection.schema }}</span>
</div>
</div>
<div v-if="connection.database">
<div class="text-ellipsis">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{ connection.database }}</span>
</div>
</div>
</div>
<div class="panel-footer text-center py-0">
<div v-if="connection.ssl" class="chip bg-success mt-2">
<i class="mdi mdi-lock mdi-18px mr-1" />
SSL
</div>
<div v-if="connection.ssh" class="chip bg-success mt-2">
<i class="mdi mdi-console-network mdi-18px mr-1" />
SSH
</div>
</div>
</div>
</div>
</TransitionGroup>
</div> </div>
</div> </div>
</div> </div>
@ -76,44 +105,61 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useFocusTrap } from '@/composables/useFocusTrap'; import { useFocusTrap } from '@/composables/useFocusTrap';
import { useConnectionsStore } from '@/stores/connections'; import { useConnectionsStore } from '@/stores/connections';
import { useWorkspacesStore } from '@/stores/workspaces'; import { useWorkspacesStore } from '@/stores/workspaces';
import { storeToRefs } from 'pinia';
const { t } = useI18n();
const connectionsStore = useConnectionsStore(); const connectionsStore = useConnectionsStore();
const workspacesStore = useWorkspacesStore(); const workspacesStore = useWorkspacesStore();
const { connections } = storeToRefs(connectionsStore); const { connections } = storeToRefs(connectionsStore);
const { getConnectionName } = connectionsStore; const { getConnectionName } = connectionsStore;
const { getWorkspace } = workspacesStore; const { selectWorkspace } = workspacesStore;
const { trapRef } = useFocusTrap(); const { trapRef } = useFocusTrap();
const emit = defineEmits(['close']); const emit = defineEmits(['close']);
const clients = new Map([
['mysql', 'MySQL'],
['maria', 'MariaDB'],
['pg', 'PostgreSQL'],
['sqlite', 'SQLite']
]);
const searchTerm = ref('');
const filteredConnections = computed(() => {
return connections.value.filter(connection => {
return connection.name?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
connection.host?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
connection.database?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
connection.databasePath?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
connection.schema?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
connection.user?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
String(connection.port)?.includes(searchTerm.value);
});
});
const closeModal = () => emit('close'); const closeModal = () => emit('close');
const onKey = (e:KeyboardEvent) => { const selectConnection = (uid: string) => {
e.stopPropagation(); selectWorkspace(uid);
if (e.key === 'Escape') closeModal();
closeModal();
}; };
const getStatusBadge = (uid: string) => { const onKey = (e:KeyboardEvent) => {
if (getWorkspace(uid)) { if (e.key === 'Escape') {
const status = getWorkspace(uid).connectionStatus; e.stopPropagation();
if ((e.target as HTMLInputElement).tagName === 'INPUT' && searchTerm.value.length > 0)
switch (status) { searchTerm.value = '';
case 'connected': else
return 'badge badge-connected'; closeModal();
case 'connecting':
return 'badge badge-connecting';
case 'failed':
return 'badge badge-failed';
default:
return '';
}
} }
}; };
@ -146,11 +192,6 @@ window.addEventListener('keydown', onKey, { capture: true });
margin-left: 0.2rem; margin-left: 0.2rem;
} }
.result-tabs {
background: transparent !important;
margin: 0;
}
.modal { .modal {
align-items: flex-start; align-items: flex-start;
@ -164,8 +205,14 @@ window.addEventListener('keydown', onKey, { capture: true });
} }
} }
.processes-toolbar { .connections-search{
display: flex; display: flex;
justify-content: space-between; justify-content: space-around;
}
.connection-block{
cursor: pointer;
transition: all .2s;
border-radius: $border-radius;
} }
</style> </style>

View File

@ -214,6 +214,19 @@ watch(unpinnedConnectionsArr, (newVal, oldVal) => {
}, 50); }, 50);
} }
}); });
watch(selectedWorkspace, (newVal, oldVal) => {
if (newVal !== oldVal) {
setTimeout(() => {
const element = document.querySelector<HTMLElement>('.settingbar-element.selected');
if (element) {
element.setAttribute('tabindex', '-1');
element.focus();
element.removeAttribute('tabindex');
}
}, 150);
}
});
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -411,12 +411,12 @@ const workspacesStore = useWorkspacesStore();
const { connectWorkspace, selectWorkspace } = workspacesStore; const { connectWorkspace, selectWorkspace } = workspacesStore;
const clients = ref([ const clients = [
{ name: 'MySQL', slug: 'mysql' }, { name: 'MySQL', slug: 'mysql' },
{ name: 'MariaDB', slug: 'maria' }, { name: 'MariaDB', slug: 'maria' },
{ name: 'PostgreSQL', slug: 'pg' }, { name: 'PostgreSQL', slug: 'pg' },
{ name: 'SQLite', slug: 'sqlite' } { name: 'SQLite', slug: 'sqlite' }
]); ];
const connection = ref({ const connection = ref({
name: '', name: '',

View File

@ -253,6 +253,14 @@
> >
</div> </div>
</div> </div>
<div class="form-group columns">
<div class="column col-4 col-sm-12" />
<div class="column col-8 col-sm-12">
<label class="form-checkbox form-inline">
<input v-model="localConnection.untrustedConnection" type="checkbox"><i class="form-icon" /> {{ t('message.untrustedConnection') }}
</label>
</div>
</div>
</fieldset> </fieldset>
</form> </form>
</div> </div>
@ -416,12 +424,12 @@ const { editConnection } = useConnectionsStore();
const { addNotification } = useNotificationsStore(); const { addNotification } = useNotificationsStore();
const { connectWorkspace } = useWorkspacesStore(); const { connectWorkspace } = useWorkspacesStore();
const clients = ref([ const clients = [
{ name: 'MySQL', slug: 'mysql' }, { name: 'MySQL', slug: 'mysql' },
{ name: 'MariaDB', slug: 'maria' }, { name: 'MariaDB', slug: 'maria' },
{ name: 'PostgreSQL', slug: 'pg' }, { name: 'PostgreSQL', slug: 'pg' },
{ name: 'SQLite', slug: 'sqlite' } { name: 'SQLite', slug: 'sqlite' }
]); ];
const firstInput: Ref<HTMLInputElement> = ref(null); const firstInput: Ref<HTMLInputElement> = ref(null);
const localConnection: Ref<ConnectionParams & { pgConnString: string }> = ref(null); const localConnection: Ref<ConnectionParams & { pgConnString: string }> = ref(null);

View File

@ -294,7 +294,8 @@ module.exports = {
missingOrIncompleteTranslation: 'Missing or incomplete translation?', missingOrIncompleteTranslation: 'Missing or incomplete translation?',
findOutHowToContribute: 'Find out how to contribute', findOutHowToContribute: 'Find out how to contribute',
disableFKChecks: 'Disable foreigh key checks', disableFKChecks: 'Disable foreigh key checks',
allConnections: 'All connections' allConnections: 'All connections',
searchForConnections: 'Search for connections'
}, },
faker: { faker: {
address: 'Address', address: 'Address',

View File

@ -256,6 +256,12 @@
} }
} }
.connection-block{
&:hover {
background: $bg-color-light-dark;
}
}
.bg-checkered { .bg-checkered {
background-image: background-image:
linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)), linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),

View File

@ -274,6 +274,12 @@
background: rgba($bg-color-light-gray, 100%); background: rgba($bg-color-light-gray, 100%);
} }
} }
.connection-block{
&:hover {
background: $bg-color-light-gray;
}
}
.context { .context {
color: $body-font-color-dark; color: $body-font-color-dark;