diff --git a/leafmap/maplibregl.py b/leafmap/maplibregl.py index 99541da933..b358c8a346 100644 --- a/leafmap/maplibregl.py +++ b/leafmap/maplibregl.py @@ -83,6 +83,10 @@ basemaps = Box(xyz_to_leaflet(), frozen_box=True) +SIDEBAR_PANEL_BACKGROUND = "#ffffff" +SIDEBAR_PANEL_TEXT_COLOR = "#212121" +SIDEBAR_SCROLLBAR_STYLE = "scrollbar-color: #bdbdbd #f5f5f5; scrollbar-width: thin;" + class Map(MapWidget): """The Map class inherits from the MapWidget class of the maplibre.ipywidget module.""" @@ -553,7 +557,9 @@ def add_to_sidebar( and self.add_floating_sidebar_flag and not hasattr(self, "floating_sidebar_content_box") ): - self.floating_sidebar_content_box = widgets.VBox(children=[]) + self.floating_sidebar_content_box = widgets.VBox( + children=[], layout=widgets.Layout(width="100%", overflow="hidden") + ) self._floating_sidebar_widgets = {} # Check if floating sidebar is being used @@ -5438,7 +5444,9 @@ def add_floating_sidebar( # Initialize floating sidebar state if not hasattr(self, "floating_sidebar_content_box"): - self.floating_sidebar_content_box = widgets.VBox(children=[]) + self.floating_sidebar_content_box = widgets.VBox( + children=[], layout=widgets.Layout(width="100%", overflow="hidden") + ) if not hasattr(self, "_floating_sidebar_widgets"): self._floating_sidebar_widgets = {} @@ -5449,25 +5457,39 @@ def add_floating_sidebar( content_widgets.extend(sidebar_content) # Create main sidebar box with layer manager and additional content - main_sidebar_box = widgets.VBox(children=content_widgets) + main_sidebar_box = widgets.VBox( + children=content_widgets, + layout=widgets.Layout(width="100%", overflow="hidden"), + ) # Create toggle button toggle_icon = v.Icon( children=["mdi-chevron-left"] if sidebar_visible else ["mdi-chevron-right"], small=True, + style_=f"color: {SIDEBAR_PANEL_TEXT_COLOR};", ) toggle_btn = v.Btn( icon=True, children=[toggle_icon], - style_="width: 22px; height: 22px; min-width: 22px; padding: 0;", + style_=( + f"width: 22px; height: 22px; min-width: 22px; padding: 0; " + f"color: {SIDEBAR_PANEL_TEXT_COLOR};" + ), ) # Create settings/wrench button - settings_icon = v.Icon(children=["mdi-wrench"], small=True) + settings_icon = v.Icon( + children=["mdi-wrench"], + small=True, + style_=f"color: {SIDEBAR_PANEL_TEXT_COLOR};", + ) settings_btn = v.Btn( icon=True, children=[settings_icon], - style_="width: 22px; height: 22px; min-width: 22px; padding: 0;", + style_=( + f"width: 22px; height: 22px; min-width: 22px; padding: 0; " + f"color: {SIDEBAR_PANEL_TEXT_COLOR};" + ), ) # Create header row with toggle and settings buttons @@ -5503,7 +5525,7 @@ class SidebarState: step=10, description="Width:", continuous_update=True, - layout=widgets.Layout(width="100%"), + layout=widgets.Layout(width="100%", min_width="0"), ) # Width change handler @@ -5518,8 +5540,10 @@ def on_width_change(change): overflow-y: auto; overflow-x: hidden; z-index: 1000; - background-color: white; + background-color: {SIDEBAR_PANEL_BACKGROUND}; + color: {SIDEBAR_PANEL_TEXT_COLOR}; border-radius: 4px; + {SIDEBAR_SCROLLBAR_STYLE} """ width_slider.observe(on_width_change, names="value") @@ -5554,7 +5578,8 @@ def on_settings_click(widget, event, data): # Combine main sidebar with dynamic content box for layer settings and other widgets sidebar_box = widgets.VBox( - children=[main_sidebar_box, self.floating_sidebar_content_box] + children=[main_sidebar_box, self.floating_sidebar_content_box], + layout=widgets.Layout(width="100%", overflow="hidden"), ) # Toggle function @@ -5577,8 +5602,10 @@ def toggle_sidebar(widget, event, data): overflow-y: auto; overflow-x: hidden; z-index: 1000; - background-color: white; + background-color: {SIDEBAR_PANEL_BACKGROUND}; + color: {SIDEBAR_PANEL_TEXT_COLOR}; border-radius: 4px; + {SIDEBAR_SCROLLBAR_STYLE} """ else: # Hide sidebar content, only show toggle button @@ -5592,9 +5619,11 @@ def toggle_sidebar(widget, event, data): max-height: none; overflow: visible; z-index: 1000; - background-color: white; + background-color: {SIDEBAR_PANEL_BACKGROUND}; + color: {SIDEBAR_PANEL_TEXT_COLOR}; border-radius: 4px; padding: 4px; + {SIDEBAR_SCROLLBAR_STYLE} """ toggle_btn.on_event("click", toggle_sidebar) @@ -5612,8 +5641,10 @@ def toggle_sidebar(widget, event, data): overflow-y: auto; overflow-x: hidden; z-index: 1000; - background-color: white; + background-color: {SIDEBAR_PANEL_BACKGROUND}; + color: {SIDEBAR_PANEL_TEXT_COLOR}; border-radius: 4px; + {SIDEBAR_SCROLLBAR_STYLE} """ else: initial_style = f""" @@ -5624,9 +5655,11 @@ def toggle_sidebar(widget, event, data): max-height: none; overflow: visible; z-index: 1000; - background-color: white; + background-color: {SIDEBAR_PANEL_BACKGROUND}; + color: {SIDEBAR_PANEL_TEXT_COLOR}; border-radius: 4px; padding: 4px; + {SIDEBAR_SCROLLBAR_STYLE} """ overlay = v.Card( @@ -8166,27 +8199,36 @@ def __init__( self.map_stack.children = [m] # Sidebar content container - self.sidebar_content_box = widgets.VBox() + self.sidebar_content_box = widgets.VBox( + layout=widgets.Layout(width="100%", overflow="hidden") + ) if sidebar_content: self.set_sidebar_content(sidebar_content) # Toggle button self.toggle_icon = v.Icon( - children=["mdi-chevron-right"] if sidebar_visible else ["mdi-chevron-left"] + children=["mdi-chevron-right"] if sidebar_visible else ["mdi-chevron-left"], + style_=f"color: {SIDEBAR_PANEL_TEXT_COLOR};", ) self.toggle_btn = v.Btn( icon=True, children=[self.toggle_icon], - style_="width: 48px; height: 48px; min-width: 48px;", + style_=( + f"width: 48px; height: 48px; min-width: 48px; " + f"color: {SIDEBAR_PANEL_TEXT_COLOR};" + ), ) self.toggle_btn.on_event("click", self.toggle_sidebar) # Settings icon - self.settings_icon = v.Icon(children=["mdi-wrench"]) + self.settings_icon = v.Icon( + children=["mdi-wrench"], + style_=f"color: {SIDEBAR_PANEL_TEXT_COLOR};", + ) self.settings_btn = v.Btn( icon=True, children=[self.settings_icon], - style_="width: 36px; height: 36px;", + style_=f"width: 36px; height: 36px; color: {SIDEBAR_PANEL_TEXT_COLOR};", ) self.settings_btn.on_event("click", self.toggle_width_slider) @@ -10264,6 +10306,7 @@ def __init__( close_icon: str = "mdi-close", label="Layers", background_color: str = "#f5f5f5", + text_color: str = SIDEBAR_PANEL_TEXT_COLOR, groups: dict = None, *args: Any, **kwargs: Any, @@ -10279,6 +10322,9 @@ def __init__( close_icon (str): The icon for the close button. Defaults to "mdi-close". label (str): The label for the layer manager. Defaults to "Layers". background_color (str): The background color of the header. Defaults to "#f5f5f5". + text_color (str): The color used for header text and icons. Defaults to + ``SIDEBAR_PANEL_TEXT_COLOR``. Override this when passing a darker + ``background_color`` so the header stays readable. groups (dict): A dictionary of layer groups, such as {"Group 1": ["layer1", "layer2"], "Group 2": ["layer3", "layer4"]}. A group layer toggle will be created for each group. Defaults to None. @@ -10317,19 +10363,32 @@ def __init__( icon=True, small=True, class_="ma-0", - style_="min-width: 24px; width: 24px;", - children=[v.Icon(children=[close_icon])], + style_=f"min-width: 24px; width: 24px; color: {text_color};", + children=[ + v.Icon( + children=[close_icon], + style_=f"color: {text_color};", + ) + ], ) close_btn.on_event("click", self._handle_close) header = v.ExpansionPanelHeader( - style_=f"height: {height}; min-height: {height}; background-color: {background_color};", + style_=( + f"height: {height}; min-height: {height}; " + f"background-color: {background_color}; " + f"color: {text_color};" + ), children=[ v.Row( align="center", class_="d-flex flex-grow-1 align-center", children=[ - v.Icon(children=[layer_icon], class_="ml-1"), + v.Icon( + children=[layer_icon], + class_="ml-1", + style_=f"color: {text_color};", + ), v.Spacer(), # push title to center v.Html(tag="span", children=[label], class_="text-subtitle-2"), v.Spacer(), # push close to right @@ -10341,16 +10400,26 @@ def __init__( ) panel = v.ExpansionPanel( + style_=( + f"background-color: {SIDEBAR_PANEL_BACKGROUND}; " + f"color: {text_color}; " + f"overflow-x: hidden; {SIDEBAR_SCROLLBAR_STYLE}" + ), children=[ header, v.ExpansionPanelContent( + style_=( + f"background-color: {SIDEBAR_PANEL_BACKGROUND}; " + f"color: {text_color}; " + f"overflow-x: hidden; {SIDEBAR_SCROLLBAR_STYLE}" + ), children=[ widgets.VBox( [self.master_toggle, self.group_toggles, self.layers_box] ) - ] + ], ), - ] + ], ) if expanded: @@ -10392,8 +10461,8 @@ def build_layer_controls(self) -> None: checkbox = widgets.Checkbox(value=visible, description=name, style=style) checkbox.layout.flex = "1 1 auto" - checkbox.layout.max_width = "200px" - checkbox.layout.min_width = "120px" + checkbox.layout.min_width = "0" + checkbox.layout.overflow = "hidden" slider = widgets.FloatSlider( value=opacity, @@ -10403,7 +10472,10 @@ def build_layer_controls(self) -> None: readout=False, tooltip="Change layer opacity", layout=widgets.Layout( - flex="1 1 auto", min_width="120px", padding=padding + flex="0 0 150px", + width="150px", + min_width="100px", + padding=padding, ), ) @@ -10557,6 +10629,7 @@ def __init__( close_icon: str = "mdi-close", label: str = "My Tools", background_color: str = "#f5f5f5", + text_color: str = SIDEBAR_PANEL_TEXT_COLOR, height: str = "40px", expanded: bool = True, host_map: Optional[Any] = None, @@ -10571,6 +10644,9 @@ def __init__( widget_icon (str): Icon for the header. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons. close_icon (str): Icon for the close button. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons. background_color (str): Background color of the header. Defaults to "#f5f5f5". + text_color (str): Color used for header text and icons. Defaults to + ``SIDEBAR_PANEL_TEXT_COLOR``. Override this when passing a darker + ``background_color`` so the header stays readable. label (str): Text label for the header. Defaults to "My Tools". height (str): Height of the header. Defaults to "40px". expanded (bool): Whether the panel is expanded by default. Defaults to True. @@ -10578,7 +10654,9 @@ def __init__( **kwargs (Any): Additional keyword arguments for the parent class. """ # Wrap content in a mutable VBox - self.content_box = widgets.VBox() + self.content_box = widgets.VBox( + layout=widgets.Layout(width="100%", overflow="hidden") + ) self.host_map = host_map if widget: if isinstance(widget, (list, tuple)): @@ -10591,19 +10669,32 @@ def __init__( icon=True, small=True, class_="ma-0", - style_="min-width: 24px; width: 24px;", - children=[v.Icon(children=[close_icon])], + style_=f"min-width: 24px; width: 24px; color: {text_color};", + children=[ + v.Icon( + children=[close_icon], + style_=f"color: {text_color};", + ) + ], ) close_btn.on_event("click", self._handle_close) header = v.ExpansionPanelHeader( - style_=f"height: {height}; min-height: {height}; background-color: {background_color};", + style_=( + f"height: {height}; min-height: {height}; " + f"background-color: {background_color}; " + f"color: {text_color};" + ), children=[ v.Row( align="center", class_="d-flex flex-grow-1 align-center", children=[ - v.Icon(children=[widget_icon], class_="ml-1"), + v.Icon( + children=[widget_icon], + class_="ml-1", + style_=f"color: {text_color};", + ), v.Spacer(), # push title to center v.Html(tag="span", children=[label], class_="text-subtitle-2"), v.Spacer(), # push close to right @@ -10615,10 +10706,22 @@ def __init__( ) self.panel = v.ExpansionPanel( + style_=( + f"background-color: {SIDEBAR_PANEL_BACKGROUND}; " + f"color: {text_color}; " + f"overflow-x: hidden; {SIDEBAR_SCROLLBAR_STYLE}" + ), children=[ header, - v.ExpansionPanelContent(children=[self.content_box]), - ] + v.ExpansionPanelContent( + style_=( + f"background-color: {SIDEBAR_PANEL_BACKGROUND}; " + f"color: {text_color}; " + f"overflow-x: hidden; {SIDEBAR_SCROLLBAR_STYLE}" + ), + children=[self.content_box], + ), + ], ) super().__init__(