4242from .utils import full_stack
4343from .catalog import DataCatalog , DataReference , CatalogView # noqa: F401 – exposed for subclasses
4444
45+ # ---------------------------------------------------------------------------
46+ # Standard DWR disclaimer — import and assign to disclaimer_text on any
47+ # DataUIManager subclass to display it in the sidebar.
48+ # ---------------------------------------------------------------------------
49+ DWR_DISCLAIMER_TEXT = (
50+ "All information provided by the Department of Water Resources on its Web "
51+ "pages and Internet sites is made available to provide immediate access for "
52+ "the convenience of interested persons. While the Department believes the "
53+ "information to be reliable, human or mechanical error remains a "
54+ "possibility. Therefore, the Department does not guarantee the accuracy, "
55+ "completeness, timeliness, or correct sequencing of the information. "
56+ "Neither the Department of Water Resources nor any of the sources of the "
57+ "information shall be responsible for any errors or omissions, or for the "
58+ "use or results obtained from the use of this information. Other specific "
59+ "cautionary notices may be included on other Web pages maintained by the "
60+ "Department."
61+ )
62+
4563# setup logger
4664logger = logging .getLogger (__name__ )
4765logger .setLevel (logging .INFO )
@@ -309,8 +327,21 @@ class DataUIManager(DataProvider):
309327 * :meth:`get_widgets`
310328 * :meth:`get_data_actions`
311329 * :meth:`get_no_selection_message`
330+ * ``disclaimer_text`` param — set to any string to show a collapsible
331+ Disclaimer card at the bottom of the sidebar (use ``DWR_DISCLAIMER_TEXT``
332+ for the standard DWR disclaimer).
312333 """
313334
335+ disclaimer_text = param .String (
336+ default = None ,
337+ allow_None = True ,
338+ doc = (
339+ "Text for a collapsible Disclaimer card shown at the bottom of the "
340+ "sidebar. Set to DWR_DISCLAIMER_TEXT for the standard DWR disclaimer, "
341+ "or any other string for a custom notice. Leave None to omit."
342+ ),
343+ )
344+
314345 @classmethod
315346 def help (cls ):
316347 """
@@ -416,13 +447,18 @@ def get_map_option_widgets(self):
416447 return None
417448
418449 def get_sidebar_disclaimer (self ):
419- """Return a Panel pane (or plain string) to display at the bottom of the
420- sidebar, or ``None`` (default) to add nothing.
450+ """Return a Panel pane with disclaimer content for the modal dialog,
451+ or ``None`` (default) to add nothing.
421452
422- Override in a subclass to inject a data-source disclaimer, terms of
423- use notice, or any other footer content .
453+ Set the ``disclaimer_text`` param to have it rendered automatically,
454+ or override this method to return a custom Panel component .
424455 """
425- return None
456+ if not self .disclaimer_text :
457+ return None
458+ return pn .pane .Markdown (
459+ f"## Disclaimer\n \n { self .disclaimer_text } " ,
460+ sizing_mode = "stretch_width" ,
461+ )
426462
427463 def get_data_actions (self ) -> list :
428464 """Return a list of default data actions. Override to customize available actions."""
@@ -902,6 +938,7 @@ def create_data_table(self, dfs):
902938
903939 self ._display_panel = pn .Row (sizing_mode = "stretch_both" )
904940 self ._action_panel = pn .Row ()
941+ self ._tab_count = 0
905942 actions = self ._dataui_manager .get_data_actions ()
906943
907944 if actions :
@@ -956,6 +993,39 @@ def about_callback(event):
956993 about_btn .on_click (about_callback )
957994 return about_btn
958995
996+ def create_disclaimer_button (self , template , disclaimer_modal_content ):
997+ """Return a button that swaps the modal to the disclaimer text and opens it."""
998+ disclaimer_btn = pn .widgets .Button (
999+ name = "Disclaimer" , button_type = "light" , icon = "alert-circle"
1000+ )
1001+
1002+ def disclaimer_callback (event ):
1003+ template .modal .clear ()
1004+ template .modal .append (disclaimer_modal_content )
1005+ template .open_modal ()
1006+
1007+ disclaimer_btn .on_click (disclaimer_callback )
1008+ return disclaimer_btn
1009+
1010+ def add_header_buttons (self , template ):
1011+ """Add About (and optionally Disclaimer) buttons to *template*'s header.
1012+
1013+ Use this when DataUI is embedded inside an outer template that supplies
1014+ its own header — the buttons will close over the correct *template* so
1015+ modals open on the servable outer template rather than the inner one
1016+ returned by :meth:`create_view`.
1017+ """
1018+ about_button = self .create_about_button (template )
1019+ disclaimer_content = self ._dataui_manager .get_sidebar_disclaimer ()
1020+ if disclaimer_content is not None :
1021+ disclaimer_button = self .create_disclaimer_button (template , disclaimer_content )
1022+ template .header .append (
1023+ pn .Row (pn .layout .HSpacer (), disclaimer_button , about_button )
1024+ )
1025+ else :
1026+ template .header .append (pn .Row (pn .layout .HSpacer (), about_button ))
1027+ template .modal .append (self .get_about_text ())
1028+
9591029 def _create_main_view (self ):
9601030 """Create the main view content based on the current view_type"""
9611031 if self .view_type == "table" :
@@ -992,6 +1062,19 @@ def hide_progress(self):
9921062 self .progress_bar .value = 0
9931063 self .progress_bar .indeterminate = False
9941064
1065+ def show_in_display_panel (self , title , content ):
1066+ """Add *content* as a closable tab in the display panel."""
1067+ if len (self ._display_panel .objects ) > 0 and isinstance (
1068+ self ._display_panel .objects [0 ], pn .Tabs
1069+ ):
1070+ tabs = self ._display_panel .objects [0 ]
1071+ tabs .append ((title , content ))
1072+ tabs .active = len (tabs ) - 1
1073+ else :
1074+ self ._display_panel .objects = [
1075+ pn .Tabs ((title , content ), closable = True )
1076+ ]
1077+
9951078 def show_map_in_display_panel (self , event ):
9961079 """Display the map in the display panel area as a closable tab"""
9971080 try :
@@ -1207,11 +1290,8 @@ def _map_callback(
12071290 sidebar_view = pn .Column (
12081291 pn .Tabs (("Options" , control_widgets ), ("Table Options" , table_options )),
12091292 self .progress_bar ,
1293+ sizing_mode = "stretch_both" ,
12101294 )
1211- # Append optional disclaimer to the bottom of the sidebar.
1212- disclaimer = self ._dataui_manager .get_sidebar_disclaimer ()
1213- if disclaimer is not None :
1214- sidebar_view .append (disclaimer )
12151295 # Create view navigation buttons.
12161296 # Nav bar is placed inside _main_view (not in template.header) so it
12171297 # is part of template.main and remains visible when DataUI is embedded
@@ -1236,7 +1316,12 @@ def _map_callback(
12361316
12371317 # About button only in the header (visible in standalone use).
12381318 about_button = self .create_about_button (template )
1239- template .header .append (pn .Row (pn .layout .HSpacer (), about_button ))
1319+ disclaimer_text = self ._dataui_manager .get_sidebar_disclaimer ()
1320+ if disclaimer_text is not None :
1321+ disclaimer_button = self .create_disclaimer_button (template , disclaimer_text )
1322+ template .header .append (pn .Row (pn .layout .HSpacer (), disclaimer_button , about_button ))
1323+ else :
1324+ template .header .append (pn .Row (pn .layout .HSpacer (), about_button ))
12401325 template .main .append (self ._main_view )
12411326
12421327 # Adding about button
0 commit comments