experiments/arena/components/side_nav.py (182 lines of code) (raw):

# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import mesop as me from state.state import AppState from components.styles import ( SIDENAV_MAX_WIDTH, SIDENAV_MIN_WIDTH, _FANCY_TEXT_GRADIENT, DEFAULT_MENU_STYLE, ) page_json = [ {"id": 0, "display": "Arena", "icon": "stadium", "route": "/"}, {"id": 1, "display": "Leaderboard", "icon": "leaderboard", "route": "/leaderboard"}, {"id": 2, "display": "History", "icon": "history", "route": "/history"}, { "id": 10, "display": "Settings", "icon": "settings", "route": "/settings", "align": "bottom", }, ] def on_sidenav_menu_click(e: me.ClickEvent): # pylint: disable=unused-argument """Side navigation menu click handler""" state = me.state(AppState) state.sidenav_open = not state.sidenav_open def navigate_to(e: me.ClickEvent): """navigate to a specific page""" s = me.state(AppState) idx = int(e.key) print(f"idx: {idx}") page = get_page_by_id(idx) if page is None: print(f"requested {idx}, but couldn't find page with that id.") return print(f"navigating to: {page}") s.current_page = page["route"] me.navigate(s.current_page) yield def get_page_by_id(page_id): """Gets the page object with the given ID. Args: page_json: A list of page objects (dictionaries). page_id: The ID of the page to retrieve. Returns: The page object (dictionary) if found, or None if not found. """ for page in page_json: if page["id"] == page_id: return page return None @me.component def sidenav(current_page: str): """Render side navigation""" app_state = me.state(AppState) # print(f"received current page: {current_page}") with me.sidenav( opened=True, style=me.Style( width=SIDENAV_MAX_WIDTH if app_state.sidenav_open else SIDENAV_MIN_WIDTH, background=me.theme_var("secondary-container"), ), ): with me.box( style=me.Style( margin=me.Margin(top=16, left=16, right=16, bottom=16), display="flex", flex_direction="column", gap=5, ), ): with me.box( style=me.Style( display="flex", flex_direction="row", gap=5, align_items="center", ), ): with me.content_button( type="icon", on_click=on_sidenav_menu_click, ): with me.box(): with me.tooltip(message="Expand menu"): me.icon(icon="menu") if app_state.sidenav_open: me.text("IMAGE ARENA", style=_FANCY_TEXT_GRADIENT) # spacer me.box(style=me.Style(height=16)) # standard pages for idx, page in enumerate(page_json): if "align" not in page: # ignore pages with alignment, handle elsewhere menu_item( idx, page["icon"], page["display"], not app_state.sidenav_open ) # settings & theme toggle with me.box(style=MENU_BOTTOM): theme_toggle_icon( 9, "light_mode", "Theme", not app_state.sidenav_open, ) menu_item(10, "settings", "Settings", not app_state.sidenav_open) MENU_BOTTOM = me.Style( display="flex", flex_direction="column", position="absolute", bottom=8, align_content="left", ) def menu_item( key: int, icon: str, text: str, minimized: bool = True, content_style: me.Style = DEFAULT_MENU_STYLE, ): """render menu item""" if minimized: # minimized with me.box( style=me.Style( display="flex", flex_direction="row", gap=5, align_items="center", ), ): with me.content_button( key=str(key), on_click=navigate_to, style=content_style, type="icon", ): with me.tooltip(message=text): me.icon(icon=icon) else: # expanded with me.content_button( key=str(key), on_click=navigate_to, style=content_style, ): with me.box( style=me.Style( display="flex", flex_direction="row", gap=5, align_items="center", ), ): me.icon(icon=icon) me.text(text) def toggle_theme(e: me.ClickEvent): # pylint: disable=unused-argument """Toggle theme event""" s = me.state(AppState) if me.theme_brightness() == "light": me.set_theme_mode("dark") s.theme_mode = "dark" else: me.set_theme_mode("light") s.theme_mode = "light" def theme_toggle_icon(key: int, icon: str, text: str, min: bool = True): """Theme toggle icon""" # THEME_TOGGLE_STYLE = me.Style(position="absolute", bottom=50, align_content="left") if min: # minimized with me.box( style=me.Style( display="flex", flex_direction="row", gap=5, align_items="center", ), ): with me.content_button( key=str(key), on_click=toggle_theme, # style=THEME_TOGGLE_STYLE, type="icon", ): with me.tooltip(message=text): me.icon( "light_mode" if me.theme_brightness() == "dark" else "dark_mode" ) else: # expanded with me.content_button( key=str(key), on_click=toggle_theme, # style=THEME_TOGGLE_STYLE, ): with me.box( style=me.Style( display="flex", flex_direction="row", gap=5, align_items="center", ), ): me.icon( "light_mode" if me.theme_brightness() == "dark" else "dark_mode" ) me.text( "Light mode" if me.theme_brightness() == "dark" else "Dark mode" )