Skip to content

Commit 68d5e0c

Browse files
authored
feat(#2589): allow to set or override group icons in the instance sidebar (#2608)
1 parent 8a5266c commit 68d5e0c

File tree

7 files changed

+57
-27
lines changed

7 files changed

+57
-27
lines changed

spring-boot-admin-docs/src/site/asciidoc/customize_ui.adoc

+18-5
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,34 @@ include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=cust
5050
<1> Name of the view and the route.
5151
<2> Path in Vue router.
5252
<3> The imported custom component, which will be rendered on the route.
53-
<4> The handle for the custom view to be shown in the top navigation bar.
54-
<5> Order for the view.
55-
<6> Using `i18n.mergeLocaleMessage` allows to add custom translations.
53+
<4> An optional group name that allows to bind views to a logical group (defaults to "none")
54+
<5> The handle for the custom view to be shown in the top navigation bar.
55+
<6> Order for the view.
56+
<7> Using `i18n.mergeLocaleMessage` allows to add custom translations.
5657

5758
Views in the top navigation bar are sorted by ascending order.
5859

5960
If new top level routes are added to the frontend, they also must be known to the backend.
6061
Add a `/META-INF/spring-boot-admin-server-ui/extensions/{name}/routes.txt` with all your new toplevel routes (one route per line).
6162

63+
Groups are used in instance sidebar to aggregate multiple views into a collapsible menu entry showing the group's name.
64+
When a group contains just a single element, the label of the view is shown instead of the group's name.
65+
66+
==== Override/Set custom group icons ====
67+
In order to override or set icons for (custom) groups you can use the `SBA.viewRegistry.setGroupIcon` function as follows:
68+
69+
[source,javascript]
70+
----
71+
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=customization-ui-groups]
72+
----
73+
<1> Name of the group to set icon for
74+
<2> Arbitrary HTML code (e.g. SVG image) that is inserted and parsed as icon.
75+
6276
[[customizing-custom-views-top-level]]
6377
==== Adding a Top-Level View ====
6478

6579
Here is a simple top level view just listing all registered applications:
6680
[source,html]
67-
6881
----
6982
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/custom.vue[lines=17..-1]
7083
----
@@ -109,8 +122,8 @@ include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/custom-endpoint.vu
109122
<2> Each instance has a preconfigured https://github.com/axios/axios[axios] instance to access the endpoints with the correct path and headers.
110123

111124
Registering the instance view works like for the top-level view with some additional properties:
112-
[source,javascript]
113125

126+
[source,javascript]
114127
----
115128
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=customization-ui-endpoint]
116129
----

spring-boot-admin-samples/spring-boot-admin-sample-custom-ui/src/index.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ SBA.use({
2727
name: "custom", //<1>
2828
path: "/custom", //<2>
2929
component: custom, //<3>
30-
handle, //<4>
31-
order: 1000, //<5>
30+
group: "custom", //<4>
31+
handle, //<5>
32+
order: 1000, //<6>
3233
});
3334
i18n.mergeLocaleMessage("en", {
3435
custom: {
35-
label: "My Extensions", //<6>
36+
label: "My Extensions", //<7>
3637
},
3738
});
3839
i18n.mergeLocaleMessage("de", {
@@ -78,3 +79,13 @@ SBA.viewRegistry.addView({
7879
}, // <3>
7980
});
8081
// end::customization-ui-endpoint[]
82+
83+
// tag::customization-ui-groups[]
84+
SBA.viewRegistry.setGroupIcon(
85+
"custom", //<1>
86+
`<svg xmlns='http://www.w3.org/2000/svg'
87+
class='h-5 mr-3'
88+
viewBox='0 0 576 512'><path d='M512 80c8.8 0 16 7.2 16 16V416c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16H512zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 208c14.2 0 27 6.1 35.8 16c8.8 9.9 24 10.7 33.9 1.9s10.7-24 1.9-33.9c-17.5-19.6-43.1-32-71.5-32c-53 0-96 43-96 96s43 96 96 96c28.4 0 54-12.4 71.5-32c8.8-9.9 8-25-1.9-33.9s-25-8-33.9 1.9c-8.8 9.9-21.6 16-35.8 16c-26.5 0-48-21.5-48-48s21.5-48 48-48zm144 48c0-26.5 21.5-48 48-48c14.2 0 27 6.1 35.8 16c8.8 9.9 24 10.7 33.9 1.9s10.7-24 1.9-33.9c-17.5-19.6-43.1-32-71.5-32c-53 0-96 43-96 96s43 96 96 96c28.4 0 54-12.4 71.5-32c8.8-9.9 8-25-1.9-33.9s-25-8-33.9 1.9c-8.8 9.9-21.6 16-35.8 16c-26.5 0-48-21.5-48-48z'/>
89+
</svg>` //<2>
90+
);
91+
// end::customization-ui-groups[]

spring-boot-admin-server-ui/src/main/frontend/composables/ViewRegistry.ts

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const emitCustomRouteAddedEvent = debounce(() => {
2222
export function useViewRegistry() {
2323
return {
2424
views: viewRegistry.views,
25+
setGroupIcon(name, icon) {
26+
viewRegistry.setGroupIcon(name, icon);
27+
},
2528
addView(viewToAdd) {
2629
const view = viewRegistry.addView(viewToAdd)[0];
2730

spring-boot-admin-server-ui/src/main/frontend/viewRegistry.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Text, VNode, h, markRaw, reactive, shallowRef, toRaw } from 'vue';
1818
import { Router, createRouter, createWebHistory } from 'vue-router';
1919

2020
import sbaConfig from './sba-config';
21-
import { VIEW_GROUP } from './views/ViewGroup.js';
21+
import { VIEW_GROUP, VIEW_GROUP_ICON } from './views/ViewGroup.js';
2222

2323
let router: Router;
2424

@@ -54,6 +54,10 @@ export default class ViewRegistry {
5454
return router;
5555
}
5656

57+
setGroupIcon(name, icon) {
58+
VIEW_GROUP_ICON[name] = icon;
59+
}
60+
5761
createRouter() {
5862
const routesKnownToBackend = sbaConfig.uiSettings.routes.map(
5963
(r) => new RegExp(`^${r.replace('/**', '(/.*)?')}$`)

spring-boot-admin-server-ui/src/main/frontend/views/ViewGroup.ts

+16
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,19 @@ export const VIEW_GROUP = {
77
NONE: 'none',
88
SECURITY: 'security',
99
};
10+
11+
export const VIEW_GROUP_ICON = {
12+
[VIEW_GROUP.WEB]:
13+
'<svg aria-hidden="true" focusable="false" data-prefix="fas" class="w-5 h-5 mr-3" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M336.5 160C322 70.7 287.8 8 248 8s-74 62.7-88.5 152h177zM152 256c0 22.2 1.2 43.5 3.3 64h185.3c2.1-20.5 3.3-41.8 3.3-64s-1.2-43.5-3.3-64H155.3c-2.1 20.5-3.3 41.8-3.3 64zm324.7-96c-28.6-67.9-86.5-120.4-158-141.6 24.4 33.8 41.2 84.7 50 141.6h108zM177.2 18.4C105.8 39.6 47.8 92.1 19.3 160h108c8.7-56.9 25.5-107.8 49.9-141.6zM487.4 192H372.7c2.1 21 3.3 42.5 3.3 64s-1.2 43-3.3 64h114.6c5.5-20.5 8.6-41.8 8.6-64s-3.1-43.5-8.5-64zM120 256c0-21.5 1.2-43 3.3-64H8.6C3.2 212.5 0 233.8 0 256s3.2 43.5 8.6 64h114.6c-2-21-3.2-42.5-3.2-64zm39.5 96c14.5 89.3 48.7 152 88.5 152s74-62.7 88.5-152h-177zm159.3 141.6c71.4-21.2 129.4-73.7 158-141.6h-108c-8.8 56.9-25.6 107.8-50 141.6zM19.3 352c28.6 67.9 86.5 120.4 158 141.6-24.4-33.8-41.2-84.7-50-141.6h-108z"></path></svg>',
14+
[VIEW_GROUP.INSIGHTS]:
15+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
16+
[VIEW_GROUP.DATA]:
17+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>',
18+
[VIEW_GROUP.JVM]:
19+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
20+
[VIEW_GROUP.LOGGING]:
21+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>',
22+
[VIEW_GROUP.NONE]: '<div class="h-5 w-5 mr-3">&nbsp;</div>',
23+
[VIEW_GROUP.SECURITY]:
24+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>',
25+
};

spring-boot-admin-server-ui/src/main/frontend/views/index.ts

-17
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { VIEW_GROUP } from './ViewGroup.js';
1716

1817
const isStorybook = Object.prototype.hasOwnProperty.call(window, 'STORIES');
1918

@@ -36,20 +35,4 @@ if (!isStorybook) {
3635
});
3736
}
3837

39-
export const VIEW_GROUP_ICON = {
40-
[VIEW_GROUP.WEB]:
41-
'<svg aria-hidden="true" focusable="false" data-prefix="fas" class="w-4 h-4 mr-3" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M336.5 160C322 70.7 287.8 8 248 8s-74 62.7-88.5 152h177zM152 256c0 22.2 1.2 43.5 3.3 64h185.3c2.1-20.5 3.3-41.8 3.3-64s-1.2-43.5-3.3-64H155.3c-2.1 20.5-3.3 41.8-3.3 64zm324.7-96c-28.6-67.9-86.5-120.4-158-141.6 24.4 33.8 41.2 84.7 50 141.6h108zM177.2 18.4C105.8 39.6 47.8 92.1 19.3 160h108c8.7-56.9 25.5-107.8 49.9-141.6zM487.4 192H372.7c2.1 21 3.3 42.5 3.3 64s-1.2 43-3.3 64h114.6c5.5-20.5 8.6-41.8 8.6-64s-3.1-43.5-8.5-64zM120 256c0-21.5 1.2-43 3.3-64H8.6C3.2 212.5 0 233.8 0 256s3.2 43.5 8.6 64h114.6c-2-21-3.2-42.5-3.2-64zm39.5 96c14.5 89.3 48.7 152 88.5 152s74-62.7 88.5-152h-177zm159.3 141.6c71.4-21.2 129.4-73.7 158-141.6h-108c-8.8 56.9-25.6 107.8-50 141.6zM19.3 352c28.6 67.9 86.5 120.4 158 141.6-24.4-33.8-41.2-84.7-50-141.6h-108z"></path></svg>',
42-
[VIEW_GROUP.INSIGHTS]:
43-
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
44-
[VIEW_GROUP.DATA]:
45-
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>',
46-
[VIEW_GROUP.JVM]:
47-
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
48-
[VIEW_GROUP.LOGGING]:
49-
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>',
50-
[VIEW_GROUP.NONE]: '',
51-
[VIEW_GROUP.SECURITY]:
52-
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>',
53-
};
54-
5538
export default views;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ import SbaButton from '@/components/sba-button.vue';
124124
import Application from '@/services/application';
125125
import Instance from '@/services/instance';
126126
import { compareBy } from '@/utils/collections';
127-
import { VIEW_GROUP_ICON } from '@/views';
127+
import { VIEW_GROUP_ICON } from '@/views/ViewGroup';
128128

129129
export default defineComponent({
130130
components: { SbaButton },

0 commit comments

Comments
 (0)