Skip to content

Commit 0257d88

Browse files
committed
feat: enhance collapsible sidebar with instance initials and toggle button
1 parent 59f04a1 commit 0257d88

4 files changed

Lines changed: 88 additions & 54 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function createInitials(input: string, maxLength: number = 3): string {
2+
const trimmed = input.trim();
3+
if (!trimmed) return '';
4+
5+
const words = trimmed
6+
.replace(/([a-z])([A-Z])/g, '$1 $2')
7+
.split(/[\s\-_]+/)
8+
.filter((word) => word.length > 0);
9+
10+
return words
11+
.slice(0, maxLength) // Limit to maxLength words
12+
.map((word) => word[0].toUpperCase())
13+
.join('');
14+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import { faSquareCaretLeft } from '@fortawesome/free-solid-svg-icons';
3+
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
4+
5+
defineEmits(['toggle-sidebar']);
6+
defineProps<{
7+
open: boolean;
8+
}>();
9+
</script>
10+
11+
<template>
12+
<button @click="() => $emit('toggle-sidebar')">
13+
<font-awesome-icon
14+
:icon="faSquareCaretLeft"
15+
:class="{
16+
'rotate-180': !open,
17+
}"
18+
/>
19+
</button>
20+
</template>

spring-boot-admin-server-ui/src/main/frontend/views/instances/shell/InstanceShell.vue

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,54 @@
1818
<div class="flex h-full">
1919
<sba-wave />
2020
<div
21-
class="flex-shrink-0 flex flex-col transition-all duration-300 ease-in-out bg-white border-r relative overflow-hidden pb-14"
21+
class="flex-shrink-0 flex flex-col transition-all duration-300 ease-in-out bg-white border-r relative overflow-hidden"
2222
:class="{
23-
'w-8': !sidebarOpen,
24-
'w-64 overflow-y-auto': sidebarOpen,
23+
'w-10': !sidebarOpen,
24+
'w-64': sidebarOpen,
2525
}"
2626
>
27-
<button
28-
aria-label="Toggle sidebar"
29-
class="bg-sba-50 mx-[0.25rem] mt-[0.25rem] p-1 text-xs"
30-
:class="{
31-
'text-center': !sidebarOpen,
32-
'text-right': sidebarOpen,
33-
}"
34-
@click="toggleSidebar"
35-
>
36-
<font-awesome-icon
37-
:icon="faAngleDoubleLeft"
38-
class="transition-all"
39-
:class="{
40-
'rotate-0': sidebarOpen,
41-
'rotate-180': !sidebarOpen,
42-
}"
27+
<div v-if="instance" class="px-1 py-1">
28+
<div
29+
v-if="!sidebarOpen"
30+
class="rounded-full bg-sba-50 text-center aspect-square flex items-center overflow-hidden text-xs"
31+
>
32+
{{ createInitials(instance.registration.name) }}
33+
</div>
34+
<div
35+
v-if="sidebarOpen"
36+
class="relative hidden md:block bg-sba-50 bg-opacity-40 text-sba-900 text-sm py-4 pl-6 pr-2 text-left overflow-hidden text-ellipsis rounded transition duration-300 ease-in-out cursor-pointer"
37+
>
38+
<router-link
39+
:to="{
40+
name: 'instances/details',
41+
params: { instanceId: instance.id },
42+
}"
43+
>
44+
<span class="overflow-hidden text-ellipsis">
45+
<div class="font-bold" v-text="instance.registration.name" />
46+
<div>
47+
<small><em v-text="instance.id" /></small>
48+
</div>
49+
</span>
50+
</router-link>
51+
<button
52+
class="absolute top-1 right-1 p-1 rounded focus:outline-none focus:ring focus:ring-sba-300"
53+
@click="toggleSidebar"
54+
>
55+
t
56+
</button>
57+
</div>
58+
</div>
59+
<div class="fex-1 overflow-y-auto">
60+
<Sidebar
61+
v-if="instance"
62+
:key="instanceId"
63+
:open="sidebarOpen"
64+
:application="application"
65+
:instance="instance"
66+
:views="sidebarViews"
4367
/>
44-
</button>
45-
<Sidebar
46-
v-if="instance"
47-
v-show="sidebarOpen"
48-
:key="instanceId"
49-
:application="application"
50-
:instance="instance"
51-
:views="sidebarViews"
52-
/>
68+
</div>
5369
</div>
5470
<main class="flex-1 overflow-y-auto relative">
5571
<router-view
@@ -62,21 +78,21 @@
6278
</template>
6379

6480
<script lang="ts" setup>
65-
import { faAngleDoubleLeft } from '@fortawesome/free-solid-svg-icons';
6681
import { computed, ref } from 'vue';
6782
import { useRoute } from 'vue-router';
6883
6984
import { useViewRegistry } from '@/composables/ViewRegistry';
7085
import { useApplicationStore } from '@/composables/useApplicationStore';
7186
import { findApplicationForInstance, findInstance } from '@/store';
72-
import Sidebar from '@/views/instances/shell/sidebar';
87+
import { createInitials } from '@/utils/createInitials';
88+
import Sidebar from '@/views/instances/shell/sidebar.vue';
7389
7490
const { applications } = useApplicationStore();
7591
const { views } = useViewRegistry();
7692
const route = useRoute();
7793
7894
const SIDEBAR_STORAGE_KEY = 'de.codecentric.spring-boot-admin.sidebar';
79-
const sidebarOpen = ref(
95+
const sidebarOpen = ref<boolean>(
8096
localStorage.getItem(SIDEBAR_STORAGE_KEY) === 'true' || true,
8197
);
8298

spring-boot-admin-server-ui/src/main/frontend/views/instances/shell/sidebar.vue

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,7 @@
1818
<aside
1919
class="flex flex-col bg-white backdrop-filter backdrop-blur-lg bg-opacity-80 transition-all"
2020
>
21-
<ul class="relative px-1 py-1">
22-
<!-- Instance info block -->
23-
<li class="relative mb-1 hidden md:block">
24-
<router-link
25-
:class="`instance-summary--${instance.statusInfo.status}`"
26-
:to="{
27-
name: 'instances/details',
28-
params: { instanceId: instance.id },
29-
}"
30-
class="instance-info-block"
31-
>
32-
<span class="overflow-hidden text-ellipsis">
33-
<span class="font-bold" v-text="instance.registration.name" /><br />
34-
<small><em v-text="instance.id" /></small>
35-
</span>
36-
</router-link>
37-
</li>
38-
21+
<ul class="relative px-1 pb-1">
3922
<!-- The actual nav -->
4023
<li
4124
v-for="group in groups"
@@ -150,7 +133,10 @@
150133
</template>
151134

152135
<script lang="ts" setup>
153-
import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons';
136+
import {
137+
faArrowUpRightFromSquare,
138+
faSquareCaretLeft,
139+
} from '@fortawesome/free-solid-svg-icons';
154140
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
155141
import { Divider } from 'primevue';
156142
import { computed, h, toRaw } from 'vue';
@@ -166,10 +152,12 @@ const props = defineProps<{
166152
views: any[];
167153
instance: Instance;
168154
application: Application;
155+
open: boolean;
169156
}>();
170157
171158
const { t } = useI18n();
172159
const route = useRoute();
160+
defineEmits(['toggle-sidebar']);
173161
174162
const customLinksFromMetadata = computed(() => {
175163
const newVar = props.instance.metadataParsed?.sidebar?.links || [];
@@ -239,10 +227,6 @@ function hasMultipleViews(group: any) {
239227
</script>
240228

241229
<style scoped>
242-
.instance-info-block {
243-
@apply bg-sba-50 bg-opacity-40 text-sba-900 flex items-center text-sm py-4 px-6 text-left overflow-hidden text-ellipsis rounded transition duration-300 ease-in-out cursor-pointer;
244-
}
245-
246230
a.navbar-link {
247231
@apply cursor-pointer;
248232
}

0 commit comments

Comments
 (0)