I saw this type of navigation in grafana, but it is in Visual studio code, youtube in the smart tv and so on. It is really comfortable to be able to switch across the different apps. I use iframes as can be seen below. It would be nice to have the same functionality that Visual Studio code, i.e. when you press the icon corresponding to the active app, the sidebar of the main app is shown or hidden, and when you press the icon corresponding to an inactive app you go to that app as it is shown in the example below.
For someone trying to dome the same stuff, here is a not so short example but fully functional. In each app function (app1,app2,app3,app4,app5) you can replace by the desired functionality
from functools import partial
import numpy as np
import panel as pn
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
template = """
{% extends base %}
{% block postamble %}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
html, body {margin: 0;
height: 100%;
overflow-y: auto;
background-color: white;}
.icon-bar { width: 80px;
background-color: #555;
height: 100%;}
.icon-bar .icon {display: block;
text-align: center;
padding: 8px;
transition: all 0.3s ease;
color: white;
font-size: 28px;
text-size-adjust: none;
height: 54px; }
.icon-bar .icon:hover {background-color: #000;
width: 120 px;}
.left{width:80px;
float:left;
height:100%;}
.right{margin-left:80px;
height:100%;}
</style>
{% endblock %}
{% block contents %}
<div class="main_div" style="height:100%">
<div class="left">
<div class="icon-bar" style="height:100%">
<div class= 'icon' ><span></span></div>
<div class= 'icon' ><span></span></div>
<div class='icon' onclick="Redirect('app1')"> <i class="fa fa-trash"></i> </div>
<div class='icon' onclick="Redirect('app2')"> <i class="fa fa-globe"></i> </div>
<div class='icon' onclick="Redirect('app3')"> <i class="fa fa-minus-circle"></i> </div>
<div class='icon' onclick="Redirect('app4')"> <i class="fa fa-envelope"></i> </div>
<div class='icon' onclick="Redirect('app5')"> <i class="fa fa-search"></i> </div>
</div>
</div>
<div class="right">
<iframe id="iframe" style="border: None;
width: 100%; height: 100%; overflow: hidden;"
src="http://localhost:5006/app1" title="W3Schools">
</iframe>
</div>
<script>
function Redirect(app){
document.getElementById('iframe').src = 'http://localhost:5006/'+app;
}
</script>
</div>
{% endblock %}
"""
def navigator():
return pn.Template(template)
def update(source):
data = np.random.randint(0, 2 ** 31, 10)
source.data.update({"y": data})
def app1():
source = ColumnDataSource({"x": range(10), "y": range(10)})
p = figure()
p.line(x="x", y="y", source=source)
vanilla = pn.template.VanillaTemplate(title='app1: Vanilla')
vanilla.main.append(pn.pane.Bokeh(p,sizing_mode='stretch_both'))
cb = pn.state.add_periodic_callback(partial(update, source), 200, timeout=5000)
return vanilla
def app2():
source = ColumnDataSource({"x": range(10), "y": range(10)})
p = figure()
p.line(x="x", y="y", source=source)
golden = pn.template.GoldenTemplate(title='app2: Golden')
golden.main.append(pn.pane.Bokeh(p,sizing_mode='stretch_both'))
cb = pn.state.add_periodic_callback(partial(update, source), 200, timeout=5000)
return golden
def app3():
source = ColumnDataSource({"x": range(10), "y": range(10)})
p = figure()
p.line(x="x", y="y", source=source)
material = pn.template.MaterialTemplate(title='app3: Material')
material.main.append(pn.pane.Bokeh(p,sizing_mode='stretch_both'))
cb = pn.state.add_periodic_callback(partial(update, source), 200, timeout=5000)
return material
def app4():
source = ColumnDataSource({"x": range(10), "y": range(10)})
p = figure()
p.line(x="x", y="y", source=source)
react = pn.template.ReactTemplate(title='app4: React')
react.main[:5,:12] = pn.pane.Bokeh(p,sizing_mode='stretch_both')
cb = pn.state.add_periodic_callback(partial(update, source), 200, timeout=5000)
return react
def app5():
source = ColumnDataSource({"x": range(10), "y": range(10)})
p = figure()
p.line(x="x", y="y", source=source)
bootstrap = pn.template.BootstrapTemplate(title='app5: Bootstrap')
bootstrap.main.append(pn.pane.Bokeh(p,sizing_mode='stretch_both'))
cb = pn.state.add_periodic_callback(partial(update, source), 200, timeout=5000)
return bootstrap
dict_apps = {'navigator':navigator,
'app1':app1,
'app2':app2,
'app3':app3,
'app4':app4,
'app5':app5}
port_bokeh = 5006
pn.serve(dict_apps, port=port_bokeh)
and it works like this. The main problem with this approach is you do not have the real location of the app.