From 3e5f2c226f49de1707bbf595649960a66edd36c4 Mon Sep 17 00:00:00 2001 From: Michael Ikemann Date: Sat, 11 May 2024 07:35:26 +0200 Subject: [PATCH] Renamed the SinglePageRouter to SinglePageRouterConfig to emphasize it is a singleton, static object created and configured once. --- examples/outlet/{ => cloud_ui}/main.py | 9 ++++---- examples/outlet/{ => cloud_ui}/services.json | 0 examples/outlet/login/main.py | 23 +++++++++++++++++++ examples/single_page_router/advanced.py | 2 +- nicegui/elements/router_frame.py | 4 ++-- nicegui/outlet.py | 8 +++---- nicegui/single_page_app.py | 13 ++++++----- ...router.py => single_page_router_config.py} | 10 ++++---- nicegui/single_page_target.py | 4 ++-- 9 files changed, 49 insertions(+), 24 deletions(-) rename examples/outlet/{ => cloud_ui}/main.py (92%) rename examples/outlet/{ => cloud_ui}/services.json (100%) create mode 100644 examples/outlet/login/main.py rename nicegui/{single_page_router.py => single_page_router_config.py} (97%) diff --git a/examples/outlet/main.py b/examples/outlet/cloud_ui/main.py similarity index 92% rename from examples/outlet/main.py rename to examples/outlet/cloud_ui/main.py index 5943112ba..031ca1fcd 100644 --- a/examples/outlet/main.py +++ b/examples/outlet/cloud_ui/main.py @@ -1,3 +1,6 @@ +# Advanced demo showing how to use the ui.outlet and outlet.view decorators to create a nested multi-page app with a +# static header, footer and menu which is shared across all pages and hidden when the user navigates to the root page. + import os from typing import Dict @@ -54,7 +57,7 @@ def main_router(url_path: str): with ui.link('', '/').style('text-decoration: none; color: inherit;') as lnk: ui.html('Nice' 'CLOUD').classes('text-h3') - menu_visible = '/services/' in url_path # the service will make the menu visible anyway - suppresses animation + menu_visible = '/services/' in url_path # make instantly visible if the initial path is a service menu_drawer = ui.left_drawer(bordered=True, value=menu_visible, fixed=True).classes('bg-primary') with ui.footer(): ui.label('Copyright 2024 by My Company') @@ -80,7 +83,6 @@ def main_app_index(menu_drawer: LeftDrawer): # main app index page ui.label(info.title) lnk.style('text-decoration: none; color: inherit;') ui.label(info.description) - ui.html('

') # add a link to the other app ui.markdown("Click [here](/other_app) to visit the other app.") @@ -89,7 +91,6 @@ def main_app_index(menu_drawer: LeftDrawer): # main app index page @main_router.outlet('/services/{service_name}') # service outlet def services_router(service_name: str, menu_drawer: LeftDrawer): service: ServiceDefinition = services[service_name] - # update menu drawer menu_drawer.clear() with menu_drawer: menu_drawer.show() @@ -134,4 +135,4 @@ def sub_service_index(sub_service: SubServiceDefinition): ui.label(sub_service.description) -ui.run(show=False) +ui.run(show=False, title='NiceCLOUD Portal') diff --git a/examples/outlet/services.json b/examples/outlet/cloud_ui/services.json similarity index 100% rename from examples/outlet/services.json rename to examples/outlet/cloud_ui/services.json diff --git a/examples/outlet/login/main.py b/examples/outlet/login/main.py new file mode 100644 index 000000000..0b7419251 --- /dev/null +++ b/examples/outlet/login/main.py @@ -0,0 +1,23 @@ +from nicegui import ui + + +@ui.outlet('/') +def main_router(url_path: str): + with ui.header(): + with ui.link('', '/').style('text-decoration: none; color: inherit;') as lnk: + ui.html('Nice' + 'CLOUD').classes('text-h3') + + +@main_router.view('/') +def main_app_index(): + # login page + ui.label('Welcome to NiceCLOUD!').classes('text-3xl') + ui.html('
') + ui.label('Username:') + ui.textbox('username') + ui.label('Password:') + ui.password('password') + + +ui.run(show=False) diff --git a/examples/single_page_router/advanced.py b/examples/single_page_router/advanced.py index aa6505063..aa52e4276 100644 --- a/examples/single_page_router/advanced.py +++ b/examples/single_page_router/advanced.py @@ -5,7 +5,7 @@ from nicegui import ui from nicegui.page import page from nicegui.single_page_app import SinglePageApp -from nicegui.single_page_router import SinglePageRouter +from nicegui.single_page_router_config import SinglePageRouterConfig @page('/', title='Welcome!') diff --git a/nicegui/elements/router_frame.py b/nicegui/elements/router_frame.py index 48de22851..53736530c 100644 --- a/nicegui/elements/router_frame.py +++ b/nicegui/elements/router_frame.py @@ -6,7 +6,7 @@ from nicegui.single_page_target import SinglePageTarget if TYPE_CHECKING: - from nicegui.single_page_router import SinglePageRouter + from nicegui.single_page_router_config import SinglePageRouterConfig class RouterFrame(ui.element, component='router_frame.js'): @@ -15,7 +15,7 @@ class RouterFrame(ui.element, component='router_frame.js'): management to prevent the browser from reloading the whole page.""" def __init__(self, - router: "SinglePageRouter", + router: "SinglePageRouterConfig", included_paths: Optional[list[str]] = None, excluded_paths: Optional[list[str]] = None, use_browser_history: bool = True, diff --git a/nicegui/outlet.py b/nicegui/outlet.py index 470ab6028..ad6a206c2 100644 --- a/nicegui/outlet.py +++ b/nicegui/outlet.py @@ -1,11 +1,11 @@ from typing import Callable, Any, Self, Optional, Generator from nicegui.client import Client -from nicegui.single_page_router import SinglePageRouter +from nicegui.single_page_router_config import SinglePageRouterConfig from nicegui.elements.router_frame import RouterFrame -class Outlet(SinglePageRouter): +class Outlet(SinglePageRouterConfig): """An outlet allows the creation of single page applications which do not reload the page when navigating between different views. The outlet is a container for multiple views and can contain nested outlets. @@ -22,7 +22,7 @@ def __init__(self, path: str, outlet_builder: Optional[Callable] = None, browser_history: bool = True, - parent: Optional['SinglePageRouter'] = None, + parent: Optional['SinglePageRouterConfig'] = None, on_instance_created: Optional[Callable] = None, **kwargs) -> None: """ @@ -122,7 +122,7 @@ def current_url(self) -> str: class OutletView: """Defines a single view / "content page" which is displayed in an outlet""" - def __init__(self, parent_outlet: SinglePageRouter, path: str, title: Optional[str] = None): + def __init__(self, parent_outlet: SinglePageRouterConfig, path: str, title: Optional[str] = None): """ :param parent_outlet: The parent outlet in which this view is displayed :param path: The path of the view, relative to the base path of the outlet diff --git a/nicegui/single_page_app.py b/nicegui/single_page_app.py index 9a0461704..ecc69b99d 100644 --- a/nicegui/single_page_app.py +++ b/nicegui/single_page_app.py @@ -4,19 +4,20 @@ from fastapi.routing import APIRoute from nicegui import core -from nicegui.single_page_router import SinglePageRouter, SinglePageRouterEntry +from nicegui.single_page_router_config import SinglePageRouterConfig, SinglePageRouterEntry class SinglePageApp: def __init__(self, - target: Union[SinglePageRouter, str], + target: Union[SinglePageRouterConfig, str], page_template: Callable[[], Generator] = None, included: Union[List[Union[Callable, str]], str, Callable] = '/*', excluded: Union[List[Union[Callable, str]], str, Callable] = '') -> None: """ - :param target: The SinglePageRouter which shall be used as the main router for the single page application. - Alternatively, you can pass the root path of the pages which shall be redirected to the single page router. + :param target: The SinglePageRouterConfig which shall be used as the main router for the single page + application. Alternatively, you can pass the root path of the pages which shall be redirected to the + single page router. :param included: Optional list of masks and callables of paths to include. Default is "/*" which includes all. If you do not want to include all relative paths, you can specify a list of masks or callables to refine the included paths. If a callable is passed, it must be decorated with a page. @@ -25,14 +26,14 @@ def __init__(self, exclusion mask. """ if isinstance(target, str): - target = SinglePageRouter(target, page_template=page_template) + target = SinglePageRouterConfig(target, page_template=page_template) self.single_page_router = target self.included: List[Union[Callable, str]] = [included] if not isinstance(included, list) else included self.excluded: List[Union[Callable, str]] = [excluded] if not isinstance(excluded, list) else excluded self.system_excluded = ['/docs', '/redoc', '/openapi.json', '_*'] def setup(self): - """Registers the SinglePageRouter with the @page decorator to handle all routes defined by the router""" + """Registers the SinglePageRouterConfig with the @page decorator to handle all routes defined by the router""" self.reroute_pages() self.single_page_router.setup_pages(force=True) diff --git a/nicegui/single_page_router.py b/nicegui/single_page_router_config.py similarity index 97% rename from nicegui/single_page_router.py rename to nicegui/single_page_router_config.py index ff0c69ac6..2c59cf148 100644 --- a/nicegui/single_page_router.py +++ b/nicegui/single_page_router_config.py @@ -45,8 +45,8 @@ def create_path_mask(path: str) -> str: return re.sub(r'{[^}]+}', '*', path) -class SinglePageRouter: - """The SinglePageRouter allows the development of a Single Page Application (SPA). +class SinglePageRouterConfig: + """The SinglePageRouterConfig allows the development of a Single Page Application (SPA). SPAs are web applications which load a single HTML page and dynamically update the content of the page. This allows faster page switches and a more dynamic user experience.""" @@ -54,7 +54,7 @@ class SinglePageRouter: def __init__(self, path: str, browser_history: bool = True, - parent: Optional["SinglePageRouter"] = None, + parent: Optional["SinglePageRouterConfig"] = None, page_template: Optional[Callable[[], Generator]] = None, on_instance_created: Optional[Callable] = None, **kwargs) -> None: @@ -78,7 +78,7 @@ def __init__(self, self.parent_router = parent if self.parent_router is not None: self.parent_router._register_child_router(self) - self.child_routers: List['SinglePageRouter'] = [] + self.child_routers: List['SinglePageRouterConfig'] = [] self.page_kwargs = kwargs def setup_pages(self, force=False) -> Self: @@ -220,6 +220,6 @@ def insert_content_area(self, content.navigate_to(initial_url, _server_side=False, sync=True) return content - def _register_child_router(self, router: 'SinglePageRouter') -> None: + def _register_child_router(self, router: 'SinglePageRouterConfig') -> None: """Registers a child router to the parent router""" self.child_routers.append(router) diff --git a/nicegui/single_page_target.py b/nicegui/single_page_target.py index 1204a4a63..69b60576b 100644 --- a/nicegui/single_page_target.py +++ b/nicegui/single_page_target.py @@ -3,7 +3,7 @@ from typing import Dict, Optional, TYPE_CHECKING, Self if TYPE_CHECKING: - from nicegui.single_page_router import SinglePageRouterEntry, SinglePageRouter + from nicegui.single_page_router_config import SinglePageRouterEntry, SinglePageRouterConfig class SinglePageTarget: @@ -15,7 +15,7 @@ def __init__(self, entry: Optional['SinglePageRouterEntry'] = None, fragment: Optional[str] = None, query_string: Optional[str] = None, - router: Optional['SinglePageRouter'] = None): + router: Optional['SinglePageRouterConfig'] = None): """ :param path: The path of the URL :param entry: Predefined entry, e.g. targeting a Callable