diff --git a/examples/components/navbar/native_streamlit_multipage/app.py b/examples/components/navbar/native_streamlit_multipage/app.py index 3bfc492..9301c6b 100644 --- a/examples/components/navbar/native_streamlit_multipage/app.py +++ b/examples/components/navbar/native_streamlit_multipage/app.py @@ -74,6 +74,15 @@ def login(): st.toast("Invalid username or password", icon="❌") +def account(): + st.write("Account page") + st.caption("This is a protected page. Only logged in users can view this.") + + +def settings(): + st.button("Theme") + + def logout(): st.session_state.logged_in = False st.session_state.app_id = None @@ -82,7 +91,8 @@ def logout(): login_page = st.Page(login, title="Log in", icon=":material/login:") -logout_page = st.Page(logout, title="Log out", icon=":material/logout:") +account_page = st.Page(account, title="Account", icon=":material/account_circle:") +settings_page = st.Page(settings, title="Settings", icon=":material/settings:") dashboard = st.Page("dashboard.py", title="Dashboard", icon=":material/dashboard:", default=True) bugs = st.Page("reports/bugs.py", title="Bug reports", icon=":material/bug_report:") alerts = st.Page("reports/alerts.py", title="System alerts", icon=":material/notification_important:") @@ -92,10 +102,10 @@ def logout(): # HERE IS THE CHANGE from streamlit_plugins.components.navbar import st_navbar, build_menu_from_st_pages -menu_data, app_map = build_menu_from_st_pages( +menu_data, menu_account_data, app_map = build_menu_from_st_pages( {"Reports": [dashboard, bugs, alerts]}, {"Tools": [search, history]}, - login_app=login_page, - logout_app=logout_page, + login_app=login_page, account_app=account_page, settings_app=settings_page, + logout_callback=logout, ) st.session_state["app_map"] = app_map @@ -113,7 +123,7 @@ def logout(): app_id = st_navbar( menu_definition=menu_data if st.session_state.logged_in else [], - login_name=logout_page.title if st.session_state.logged_in else login_page.title, + login_name=menu_account_data, hide_streamlit_markers=False, override_app_selected_id=st.session_state.app_id, sticky_nav=sticky_nav, # at the top or not diff --git a/examples/framework/multi_page.py b/examples/framework/multi_page.py index bbd662f..0f63703 100644 --- a/examples/framework/multi_page.py +++ b/examples/framework/multi_page.py @@ -4,7 +4,7 @@ def run(): multi_app = MultiApp( title="Demo", nav_horizontal=True, layout='wide', favicon="📚", - use_navbar=True, navbar_sticky=True, navbar_mode="top", + use_navbar=False, navbar_sticky=True, navbar_mode="top", use_cookie_cache=True, sidebar_state='auto', navbar_animation=True, allow_url_nav=True, hide_streamlit_markers=False, use_banner_images=None, banner_spacing=None, clear_cross_app_sessions=True, session_params=None, @@ -57,9 +57,9 @@ def run(self): @multi_app.addapp(title="Home", is_home=True) def my_home(): st.info('HOME') - if st.button('Demo1'): + if st.button('Demo1', key="change-app_demo1"): multi_app.change_app(demo1_app.get_id()) - if st.button('Demo2'): + if st.button('Demo2', key="change-app_demo2"): multi_app.change_app(demo2_app.get_id()) multi_app.add_app(title="Demo1", app=demo1_app, icon=None) diff --git a/streamlit_plugins/components/navbar/__init__.py b/streamlit_plugins/components/navbar/__init__.py index ae95aec..4680d7e 100644 --- a/streamlit_plugins/components/navbar/__init__.py +++ b/streamlit_plugins/components/navbar/__init__.py @@ -1,5 +1,5 @@ import os -from typing import Literal +from typing import Literal, Callable import streamlit as st import streamlit.components.v1 as components @@ -324,14 +324,20 @@ MATERIAL_ICON_USER_CIRCLE = ":material/account_circle:" -def build_menu_from_st_pages(*pages: StreamlitPage | dict, login_app: StreamlitPage = None, - logout_app: StreamlitPage = None) -> tuple[list[dict], dict[str, StreamlitPage]]: +def build_menu_from_st_pages( + *pages: StreamlitPage | dict, + login_app: StreamlitPage = None, logout_callback: Callable = None, account_app: StreamlitPage = None, + settings_app: StreamlitPage = None +) -> tuple[list[dict], dict, dict[str, StreamlitPage]]: + if login_app and not logout_callback: + raise ValueError("You must provide a logout callback if you provide a login app") + menu = [] app_map = {} for page in pages: if isinstance(page, dict): for label, sub_pages in page.items(): - submenu, sub_app_map = build_menu_from_st_pages(*sub_pages) + submenu, _, sub_app_map = build_menu_from_st_pages(*sub_pages) menu.append({ 'id': label.lower().replace(" ", "_"), 'label': label, @@ -350,10 +356,32 @@ def build_menu_from_st_pages(*pages: StreamlitPage | dict, login_app: StreamlitP if login_app: app_map["app_login"] = login_app - if logout_app: - app_map["app_logout"] = logout_app - - return menu, app_map + account_login_definition = { + 'id': "account_menu", + 'label': "Account", + 'icon': MATERIAL_ICON_USER_CIRCLE, + 'ttip': "Account", 'style': {}, + 'submenu': [] + } + if account_app and logout_callback: + account_login_definition['submenu'].append( + {'label': "Profile", 'id': "app_account_profile", 'icon': MATERIAL_ICON_USER_CIRCLE, 'ttip': "Profile"}, + ) + app_map["app_account_profile"] = account_app + + if settings_app: + account_login_definition['submenu'].append( + {'label': "Settings", 'id': "app_account_settings", 'icon': ":material/settings:", 'ttip': "Settings"} + ) + app_map["app_account_settings"] = settings_app + + if logout_callback: + account_login_definition['submenu'].append( + {'label': "Logout", 'id': "app_logout", 'icon': MATERIAL_ICON_LOGOUT, 'ttip': "Logout"} + ) + app_map["app_logout"] = st.Page(logout_callback, title="Log out", icon=MATERIAL_ICON_LOGOUT) + + return menu, account_login_definition, app_map def st_navbar( diff --git a/streamlit_plugins/framework/multilit/multilit_app.py b/streamlit_plugins/framework/multilit/multilit_app.py index eb24deb..09b4503 100644 --- a/streamlit_plugins/framework/multilit/multilit_app.py +++ b/streamlit_plugins/framework/multilit/multilit_app.py @@ -31,7 +31,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.exit_fn() -class MultiApp(object): +class MultiApp: """ Class to create a host application for combining multiple streamlit applications. """