diff --git a/sepal_ui/frontend/css/custom.css b/sepal_ui/frontend/css/custom.css
index d8eed29a..6dbda56f 100644
--- a/sepal_ui/frontend/css/custom.css
+++ b/sepal_ui/frontend/css/custom.css
@@ -19,10 +19,6 @@ main.v-content {
padding-top: 0px !important;
}
-/* remove all the backgrounds from the controls and widget to be colored naturelly by the map */
-.leaflet-control-container .vuetify-styles .v-application {
- background: rgb(0, 0, 0, 0);
-}
.v-alert__wrapper .progress {
background-color: transparent;
}
diff --git a/sepal_ui/frontend/json/file_icons.json b/sepal_ui/frontend/json/file_icons.json
index eef512cf..3cf63e6b 100644
--- a/sepal_ui/frontend/json/file_icons.json
+++ b/sepal_ui/frontend/json/file_icons.json
@@ -1,21 +1,21 @@
{
- "": { "color": ["#ffca28", "#ffc107"], "icon": "fa-regular fa-folder" },
- ".csv": { "color": ["#4caf50", "#00c853"], "icon": "fa-solid fa-table" },
- ".txt": { "color": ["#4caf50", "#00c853"], "icon": "fa-solid fa-table" },
- ".tif": { "color": ["#9c27b0", "#673ab7"], "icon": "fa-regular fa-image" },
- ".tiff": { "color": ["#9c27b0", "#673ab7"], "icon": "fa-regular fa-image" },
- ".vrt": { "color": ["#9c27b0", "#673ab7"], "icon": "fa-regular fa-image" },
+ "": { "color": "primary_contrast", "icon": "fa-regular fa-folder" },
+ ".csv": { "color": "secondary_contrast", "icon": "fa-solid fa-table" },
+ ".txt": { "color": "secondary_contrast", "icon": "fa-solid fa-table" },
+ ".tif": { "color": "secondary_contrast", "icon": "fa-regular fa-image" },
+ ".tiff": { "color": "secondary_contrast", "icon": "fa-regular fa-image" },
+ ".vrt": { "color": "secondary_contrast", "icon": "fa-regular fa-image" },
".shp": {
- "color": ["#9c27b0", "#673ab7"],
+ "color": "secondary_contrast",
"icon": "fa-solid fa-vector-square"
},
".geojson": {
- "color": ["#9c27b0", "#673ab7"],
+ "color": "secondary_contrast",
"icon": "fa-solid fa-vector-square"
},
- "DEFAULT": { "color": ["#00bcd4", "#03a9f4"], "icon": "fa-regular fa-file" },
+ "DEFAULT": { "color": "anchor", "icon": "fa-regular fa-file" },
"PARENT": {
- "color": ["#424242", "#ffffff"],
+ "color": "anchor",
"icon": "fa-regular fa-folder-open"
}
}
diff --git a/sepal_ui/frontend/json/progress_bar.json b/sepal_ui/frontend/json/progress_bar.json
deleted file mode 100644
index 50696a42..00000000
--- a/sepal_ui/frontend/json/progress_bar.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "color": ["#2196f3", "#3f51b5"]
-}
diff --git a/sepal_ui/frontend/styles.py b/sepal_ui/frontend/styles.py
index 679c9ade..696980a8 100644
--- a/sepal_ui/frontend/styles.py
+++ b/sepal_ui/frontend/styles.py
@@ -2,11 +2,13 @@
from pathlib import Path
from types import SimpleNamespace
-from typing import Dict, Tuple
+from typing import Tuple
import ipyvuetify as v
from IPython.display import HTML, Javascript, display
-from traitlets import Bool, HasTraits, observe
+from ipyvuetify._version import semver
+from ipywidgets import Widget
+from traitlets import Bool, HasTraits, Unicode, link
import sepal_ui.scripts.utils as su
from sepal_ui.conf import config
@@ -31,48 +33,101 @@
# define all the colors that we want to use in the theme
#
-DARK_THEME: Dict[str, str] = {
- "primary": "#b3842e",
- "accent": "#a1458e",
- "secondary": "#324a88",
- "success": "#3f802a",
- "info": "#79b1c9",
- "warning": "#b8721d",
- "error": "#a63228",
- "main": "#24221f", # Are not traits
- "darker": "#1a1a1a", # Are not traits
- "bg": "#121212", # Are not traits
- "menu": "#424242", # Are not traits
-}
-"colors used for the dark theme"
-
-LIGHT_THEME: Dict[str, str] = {
- "primary": v.theme.themes.light.primary,
- "accent": v.theme.themes.light.accent,
- "secondary": v.theme.themes.light.secondary,
- "success": v.theme.themes.light.success,
- "info": v.theme.themes.light.info,
- "warning": v.theme.themes.light.warning,
- "error": v.theme.themes.light.error,
- "main": "#2e7d32",
- "darker": "#005005",
- "bg": "#FFFFFF",
- "menu": "#FFFFFF",
-}
-"colors used for the light theme"
TYPES: Tuple[str, ...] = (
"info",
"primary",
+ "primary_contarst",
"secondary",
+ "secondary_contrast",
"accent",
"error",
"success",
"warning",
"anchor",
+ "main",
+ "darker",
+ "bg",
+ "menu",
)
"The different types defined by ipyvuetify"
+
+class ThemeColors(Widget):
+
+ _model_name = Unicode("ThemeColorsModel").tag(sync=True)
+
+ _model_module = Unicode("jupyter-vuetify").tag(sync=True)
+
+ _view_module_version = Unicode(semver).tag(sync=True)
+
+ _model_module_version = Unicode(semver).tag(sync=True)
+
+ _theme_name = Unicode().tag(sync=True)
+
+ primary = Unicode().tag(sync=True)
+ primary_contrast = Unicode().tag(sync=True)
+ secondary = Unicode().tag(sync=True)
+ secondary_contrast = Unicode().tag(sync=True)
+ accent = Unicode().tag(sync=True)
+ error = Unicode().tag(sync=True)
+ info = Unicode().tag(sync=True)
+ success = Unicode().tag(sync=True)
+ warning = Unicode().tag(sync=True)
+ anchor = Unicode(None, allow_none=True).tag(sync=True)
+ main = Unicode().tag(sync=True)
+ bg = Unicode().tag(sync=True)
+ menu = Unicode().tag(sync=True)
+ darker = Unicode().tag(sync=True)
+
+
+dark_theme_colors = ThemeColors(
+ _theme_name="dark",
+ primary="#76591e",
+ primary_contrast="#bf8f2d", # a bit lighter than the primary color
+ secondary="#363e4f",
+ secondary_contrast="#5d76ab",
+ error="#a63228",
+ info="#c5c6c9",
+ success="#3f802a",
+ warning="#b8721d",
+ accent="#272727",
+ anchor="#f3f3f3",
+ main="#24221f",
+ darker="#1a1a1a",
+ bg="#121212",
+ menu="#424242",
+)
+
+light_theme_colors = ThemeColors(
+ _theme_name="light",
+ primary="#5BB624",
+ primary_contrast="#76b353",
+ accent="#f3f3f3",
+ anchor="#f3f3f3",
+ secondary="#2199C4",
+ secondary_contrast="#5d76ab",
+ success=v.theme.themes.light.success,
+ info=v.theme.themes.light.info,
+ warning=v.theme.themes.light.warning,
+ error=v.theme.themes.light.error,
+ main="#2196f3", # used by appbar and versioncard
+ darker="#ffffff", # used for the navdrawer
+ bg="#FFFFFF",
+ menu="#FFFFFF",
+)
+
+DARK_THEME = {k: v for k, v in dark_theme_colors.__dict__["_trait_values"].items() if k in TYPES}
+"colors used for the dark theme"
+
+LIGHT_THEME = {k: v for k, v in light_theme_colors.__dict__["_trait_values"].items() if k in TYPES}
+"colors used for the light theme"
+
+
+# override the default theme with the custom ones
+v.theme.themes.light = light_theme_colors
+v.theme.themes.dark = dark_theme_colors
+
################################################################################
# define classes and method to make the application responsive
#
@@ -92,47 +147,29 @@ class SepalColor(HasTraits, SimpleNamespace):
_dark_theme: Bool = Bool(True if get_theme() == "dark" else False).tag(sync=True)
"Whether to use dark theme or not. By changing this value, the theme value will be stored in the conf file. Is only intended to be accessed in development mode."
- new_colors: dict = {}
- "Dictionary with name:color structure."
-
- @observe("_dark_theme")
- def __init__(self, *_, **new_colors) -> None:
- """Custom simple name space to store and access to the sepal_ui colors and with a magic method to display theme.
+ def __init__(self) -> None:
+ """Custom simple name space to store and access to the sepal_ui colors and with a magic method to display theme."""
+ link((self, "_dark_theme"), (v.theme, "dark"))
+ v.theme.observe(lambda *x: self.set_colors(), "dark")
- Args:
- **new_colors (optional): the new colors to set in hexadecimal as a dict (experimental)
- """
- # set vuetify theme
- v.theme.dark = self._dark_theme
+ self.set_colors()
+ def set_colors(self) -> None:
+ """Set the current hexadecimal color in the object."""
# Get get current theme name
self.theme_name = "dark" if self._dark_theme else "light"
# Save "new" theme in configuration file
su.set_config("theme", self.theme_name)
- self.kwargs = DARK_THEME if self._dark_theme else LIGHT_THEME
- self.kwargs = new_colors or self.kwargs
-
- # Even if the theme.themes.dark_theme trait could trigger the change on all elms
- # we have to replace the default values every time:
- theme = getattr(v.theme.themes, self.theme_name)
-
- # TODO: Would be awesome to find a way to create traits for the new colors and
- # assign them here directly
- [setattr(theme, color_name, color) for color_name, color in self.kwargs.items()]
-
- # Now instantiate the namespace
- SimpleNamespace.__init__(self, **self.kwargs)
- HasTraits.__init__(self)
-
- return
+ self.colors_dict = DARK_THEME if self._dark_theme else LIGHT_THEME
+ SimpleNamespace.__init__(self, **self.colors_dict)
def _repr_html_(self, *_) -> str:
"""Rich display of the color palette in an HTML frontend."""
s = 60
html = f"
Current theme: {self.theme_name}
"
- items = {k: v for k, v in self.kwargs.items()}.items()
+ items = {k: v for k, v in self.colors_dict.items()}.items()
for name, color in items:
c = su.to_colors(color)
diff --git a/sepal_ui/mapping/inspector_control.py b/sepal_ui/mapping/inspector_control.py
index 85e02bcf..35931965 100644
--- a/sepal_ui/mapping/inspector_control.py
+++ b/sepal_ui/mapping/inspector_control.py
@@ -1,6 +1,5 @@
"""Customized ``Control`` to display the value of all available layers on a specific pixel."""
-import json
from pathlib import Path
from typing import Optional, Sequence, Union
@@ -15,9 +14,7 @@
from shapely import geometry as sg
from traitlets import Bool
-from sepal_ui import color
from sepal_ui import sepalwidgets as sw
-from sepal_ui.frontend import styles as ss
from sepal_ui.mapping.layer import EELayer
from sepal_ui.mapping.menu_control import MenuControl
from sepal_ui.message import ms
@@ -61,11 +58,9 @@ def __init__(self, m: Map, open_tree: bool = True, **kwargs) -> None:
# create a loading to place it on top of the card. It will always be visible
# even when the card is scrolled
- p_style = json.loads((ss.JSON_DIR / "progress_bar.json").read_text())
self.w_loading = sw.ProgressLinear(
indeterminate=False,
- background_color=color.menu,
- color=p_style["color"][v.theme.dark],
+ background_color="menu",
)
# set up the content
diff --git a/sepal_ui/mapping/layers_control.py b/sepal_ui/mapping/layers_control.py
index 326888b4..a5e1a090 100644
--- a/sepal_ui/mapping/layers_control.py
+++ b/sepal_ui/mapping/layers_control.py
@@ -1,15 +1,12 @@
"""Extend functionalities of the ipyleaflet layer control."""
-import json
+
from types import SimpleNamespace
from typing import Optional
-import ipyvuetify as v
from ipyleaflet import GeoJSON, Map, TileLayer
from ipywidgets import link
-from sepal_ui import color
from sepal_ui import sepalwidgets as sw
-from sepal_ui.frontend import styles as ss
from sepal_ui.mapping.menu_control import MenuControl
from sepal_ui.message import ms
@@ -82,7 +79,7 @@ def __init__(self, layer: TileLayer) -> None:
"""
# create the checkbox, by default layer are visible
self.w_checkbox = sw.SimpleCheckbox(
- v_model=True, small=True, label=layer.name, color=color.primary
+ v_model=True, small=True, label=layer.name, color="primary"
)
kwargs = {"style": "width: 10%;", "tag": "td"}
checkbox_cell = sw.Html(children=[self.w_checkbox], **kwargs)
@@ -128,7 +125,7 @@ def __init__(self, layer: TileLayer) -> None:
"""
# create the checkbox, by default layer are visible
self.w_checkbox = sw.SimpleCheckbox(
- v_model=True, small=True, label=layer.name, color=color.primary
+ v_model=True, small=True, label=layer.name, color="primary"
)
kwargs = {"style": "width: 10%;", "tag": "td"}
checkbox_cell = sw.Html(children=[self.w_checkbox], **kwargs)
@@ -170,11 +167,9 @@ def __init__(self, m: Map, **kwargs) -> None:
# create a loading to place it on top of the card. It will always be visible
# even when the card is scrolled
- p_style = json.loads((ss.JSON_DIR / "progress_bar.json").read_text())
self.w_loading = sw.ProgressLinear(
indeterminate=False,
- background_color=color.menu,
- color=p_style["color"][v.theme.dark],
+ background_color="menu",
)
self.tile = sw.Tile("nested", "")
diff --git a/sepal_ui/mapping/map_btn.py b/sepal_ui/mapping/map_btn.py
index 5266d7bf..ee84cea1 100644
--- a/sepal_ui/mapping/map_btn.py
+++ b/sepal_ui/mapping/map_btn.py
@@ -2,7 +2,6 @@
import ipyvuetify as v
-from sepal_ui import color
from sepal_ui import sepalwidgets as sw
@@ -24,9 +23,7 @@ def __init__(self, content: str, **kwargs) -> None:
content = content[: min(3, len(content))].upper()
# some parameters are overloaded to match the map requirements
- kwargs["color"] = "text-color"
kwargs["outlined"] = True
- kwargs["style_"] = f"background: {color.bg};"
kwargs["children"] = [content]
kwargs["icon"] = False
kwargs.setdefault("class_", "v-map-btn")
diff --git a/sepal_ui/mapping/menu_control.py b/sepal_ui/mapping/menu_control.py
index b1c74c2f..6b220dd3 100644
--- a/sepal_ui/mapping/menu_control.py
+++ b/sepal_ui/mapping/menu_control.py
@@ -7,7 +7,6 @@
from traitlets import Bool, Int
from typing_extensions import Self
-from sepal_ui import color
from sepal_ui import sepalwidgets as sw
from sepal_ui.mapping.map_btn import MapBtn
@@ -170,7 +169,6 @@ def activate(self, *args) -> None:
"""Change the background color of the btn with respect to the status."""
# grey is contrasted enough for both light and dark theme
# could be customized further if requested
- bg_color = "gray" if self.menu.v_model is True else color.bg
- self.menu.v_slots[0]["children"].style_ = f"background: {bg_color};"
+ self.menu.v_slots[0]["children"].style_ = "background: gray;" if self.menu.v_model else ""
return
diff --git a/sepal_ui/mapping/sepal_map.py b/sepal_ui/mapping/sepal_map.py
index 0928dee6..7e568032 100644
--- a/sepal_ui/mapping/sepal_map.py
+++ b/sepal_ui/mapping/sepal_map.py
@@ -150,6 +150,17 @@ def __init__(
self._id = "".join(random.choice(string.ascii_lowercase) for i in range(6))
self.add_class(self._id)
+ v.theme.observe(self._on_theme_change, "dark")
+
+ def _on_theme_change(self, _) -> None:
+ """Change the url of the basemaps."""
+ light = "https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png"
+ dark = "https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png"
+
+ for layer in self.layers:
+ if layer.base and layer.url in [light, dark]:
+ layer.url = dark if v.theme.dark is True else light
+
@deprecated(version="2.8.0", reason="the local_layer stored list has been dropped")
def _remove_local_raster(self, local_layer: str) -> Self:
"""Remove local layer from memory.
diff --git a/sepal_ui/sepalwidgets/app.py b/sepal_ui/sepalwidgets/app.py
index e99540e5..106e0494 100644
--- a/sepal_ui/sepalwidgets/app.py
+++ b/sepal_ui/sepalwidgets/app.py
@@ -24,7 +24,6 @@
from traitlets import link, observe
from typing_extensions import Self
-from sepal_ui import color
from sepal_ui.frontend.resize_trigger import ResizeTrigger, rt
from sepal_ui.frontend.styles import get_theme
from sepal_ui.message import ms
@@ -99,7 +98,7 @@ def __init__(self, translator: Optional[Translator] = None, **kwargs) -> None:
self.language_list = v.List(
dense=True,
flat=True,
- color=color.menu,
+ color="menu",
v_model=True,
max_height="300px",
style_="overflow: auto; border-radius: 0 0 0 0;",
@@ -206,19 +205,21 @@ def __init__(self, **kwargs) -> None:
def toggle_theme(self, *args) -> None:
"""Toggle the btn icon from dark to light and adapt the configuration file."""
# use a cycle to go through the themes
+
theme_cycle = cycle(self.THEME_ICONS.keys())
next(t for t in theme_cycle if t == self.theme)
self.theme = next(t for t in theme_cycle)
+ v.theme.dark = self.theme == "dark"
+
# change icon
- self.color = "info"
self.children[0].children = [self.THEME_ICONS[self.theme]]
# change the parameter file
su.set_config("theme", self.theme)
- # trigger other events by changing v_model
- self.v_model = self.theme
+ # # trigger other events by changing v_model
+ # self.v_model = self.theme
return
@@ -251,7 +252,7 @@ def __init__(
"""
self.toggle_button = v.Btn(
icon=True,
- children=[v.Icon(class_="white--text", children=["fa-solid fa-ellipsis-v"])],
+ children=[v.Icon(class_="white--text", children=["fa-solid fa-bars"])],
)
self.title = v.ToolbarTitle(children=[title])
@@ -260,8 +261,8 @@ def __init__(
self.theme = ThemeSelect()
# set the default parameters
- kwargs.setdefault("color", color.main)
kwargs.setdefault("class_", "white--text")
+ kwargs.setdefault("color", "main")
kwargs.setdefault("dense", True)
kwargs["app"] = True
kwargs["children"] = [
@@ -328,8 +329,8 @@ def __init__(
icon = icon if icon else "fa-regular fa-folder"
children = [
- v.ListItemAction(children=[v.Icon(class_="white--text", children=[icon])]),
- v.ListItemContent(children=[v.ListItemTitle(class_="white--text", children=[title])]),
+ v.ListItemAction(children=[v.Icon(children=[icon])]),
+ v.ListItemContent(children=[v.ListItemTitle(children=[title])]),
]
# set default parameters
@@ -351,7 +352,7 @@ def __init__(
# cannot be set as a class member because it will be shared with all
# the other draweritems.
self.alert_badge = v.ListItemAction(
- children=[v.Icon(children=["fa-solid fa-circle"], x_small=True, color="red")]
+ children=[v.Icon(children=["fa-solid fa-circle"], x_small=True, color="error")]
)
if model:
@@ -465,7 +466,7 @@ def __init__(
v_slots = [{"name": "append", "children": [version_card]}]
children = [
- v.List(dense=True, children=self.items),
+ v.List(dense=True, class_="pa-0", children=self.items),
v.Divider(),
v.List(dense=True, children=code_link),
]
@@ -473,7 +474,7 @@ def __init__(
# set default parameters
kwargs.setdefault("v_model", True)
kwargs["app"] = True
- kwargs.setdefault("color", color.darker)
+ kwargs.setdefault("color", "darker")
kwargs["children"] = children
kwargs.setdefault("v_slots", v_slots)
@@ -524,8 +525,7 @@ def __init__(self, text: str = "", **kwargs) -> None:
text = text if text != "" else "SEPAL \u00A9 {}".format(datetime.today().year)
# set default parameters
- kwargs.setdefault("color", color.main)
- kwargs.setdefault("class_", "white--text")
+ kwargs.setdefault("color", "main")
kwargs["app"] = True
kwargs["children"] = [text]
@@ -607,7 +607,7 @@ def __init__(
app_children.append(self.footer)
# create a negative overlay to force the background color
- bg = v.Overlay(color=color.bg, opacity=1, style_="transition:unset", z_index=-1)
+ bg = v.Overlay(color="bg", opacity=1, style_="transition:unset", z_index=-1)
# set default parameters
kwargs.setdefault("_metadata", {"mount_id": "content"})
@@ -625,7 +625,6 @@ def __init__(
# add js event
self.appBar.locale.observe(self._locale_info, "value")
- self.appBar.theme.observe(self._theme_info, "v_model")
def show_tile(self, name: str) -> Self:
"""Select the tile to display when the app is launched.
@@ -706,14 +705,6 @@ def _locale_info(self, change: dict) -> None:
return
- def _theme_info(self, change: dict) -> None:
- """Display information about the theme change."""
- if change["new"] != "":
- msg = ms.theme.change.format(change["new"])
- self.add_banner(msg)
-
- return
-
def _remove_banner(self, change: dict) -> None:
"""Remove banner and adapt display of the others.
@@ -792,14 +783,15 @@ def VersionCard(repo_folder: str = Path.cwd()) -> Optional[v.Card]:
w_version = v.Card(
class_="text-center",
+ height="48px",
tile=True,
- color=color.main,
+ color="accent",
children=[
v.CardText(
children=[
ms.widgets.navdrawer.changelog.version.format(app_version),
w_changelog,
- ]
+ ],
),
],
)
diff --git a/sepal_ui/sepalwidgets/inputs.py b/sepal_ui/sepalwidgets/inputs.py
index e85db44b..c2da3687 100644
--- a/sepal_ui/sepalwidgets/inputs.py
+++ b/sepal_ui/sepalwidgets/inputs.py
@@ -26,7 +26,6 @@
from traitlets import link, observe
from typing_extensions import Self
-from sepal_ui import color
from sepal_ui.frontend import styles as ss
from sepal_ui.message import ms
from sepal_ui.scripts import decorator as sd
@@ -249,16 +248,14 @@ def __init__(
v_model="",
)
- p_style = json.loads((ss.JSON_DIR / "progress_bar.json").read_text())
self.loading = v.ProgressLinear(
indeterminate=False,
- background_color=color.menu,
- color=p_style["color"][v.theme.dark],
+ background_color="menu",
)
self.file_list = v.List(
dense=True,
- color=color.menu,
+ color="menu",
flat=True,
v_model=True,
max_height="300px",
@@ -418,13 +415,13 @@ def _get_items(self) -> List[v.ListItem]:
for el in list_dir:
if el.is_dir():
icon = self.ICON_STYLE[""]["icon"]
- color = self.ICON_STYLE[""]["color"][v.theme.dark]
+ color = self.ICON_STYLE[""]["color"]
elif el.suffix in self.ICON_STYLE.keys():
icon = self.ICON_STYLE[el.suffix]["icon"]
- color = self.ICON_STYLE[el.suffix]["color"][v.theme.dark]
+ color = self.ICON_STYLE[el.suffix]["color"]
else:
icon = self.ICON_STYLE["DEFAULT"]["icon"]
- color = self.ICON_STYLE["DEFAULT"]["color"][v.theme.dark]
+ color = self.ICON_STYLE["DEFAULT"]["color"]
children = [
v.ListItemAction(children=[v.Icon(color=color, children=[icon])]),
@@ -447,7 +444,7 @@ def _get_items(self) -> List[v.ListItem]:
v.ListItemAction(
children=[
v.Icon(
- color=self.ICON_STYLE["PARENT"]["color"][v.theme.dark],
+ color=self.ICON_STYLE["PARENT"]["color"],
children=[self.ICON_STYLE["PARENT"]["icon"]],
)
]
diff --git a/sepal_ui/sepalwidgets/tile.py b/sepal_ui/sepalwidgets/tile.py
index 580e2c54..0558ac49 100644
--- a/sepal_ui/sepalwidgets/tile.py
+++ b/sepal_ui/sepalwidgets/tile.py
@@ -198,6 +198,16 @@ def __init__(self) -> None:
This tile will have the "about_widget" id and "Disclaimer" title.
"""
+ super().__init__("about_tile", "Disclaimer")
+
+ self.card = v.Card(class_="pa-5", raised=True, xs12=True, children=[])
+ self.set_disclaimer()
+ v.theme.observe(self.set_disclaimer, "dark")
+
+ self.children = [self.card]
+
+ def set_disclaimer(self, _=None) -> List[Markdown]:
+ """Rebuild the disclaimer element when the theme changes."""
# create the tile content on the fly
disclaimer = " \n".join(ms.disclaimer.p)
disclaimer += " \n"
@@ -216,6 +226,4 @@ def __init__(self) -> None:
# close the file
disclaimer += ""
- content = Markdown(disclaimer)
-
- super().__init__("about_tile", "Disclaimer", inputs=[content])
+ self.card.children = [self.title] + [Markdown(disclaimer)]
diff --git a/sepal_ui/sepalwidgets/widget.py b/sepal_ui/sepalwidgets/widget.py
index f13f8eee..667089e6 100644
--- a/sepal_ui/sepalwidgets/widget.py
+++ b/sepal_ui/sepalwidgets/widget.py
@@ -19,7 +19,6 @@
from markdown import markdown
from traitlets import link, observe
-from sepal_ui import color
from sepal_ui.model import Model
from sepal_ui.sepalwidgets.sepalwidget import SepalWidget, Tooltip
@@ -140,8 +139,8 @@ def __init__(
# init the states
default_states = {
- "valid": ("Valid", color.success),
- "non_valid": ("Not valid", color.error),
+ "valid": ("Valid", "success"),
+ "non_valid": ("Not valid", "error"),
}
self.states = default_states if not states else states
diff --git a/sepal_ui/templates/panel_app/component/message/en/app.json b/sepal_ui/templates/panel_app/component/message/en/app.json
index 17776219..bf2d88cb 100644
--- a/sepal_ui/templates/panel_app/component/message/en/app.json
+++ b/sepal_ui/templates/panel_app/component/message/en/app.json
@@ -4,7 +4,8 @@
"footer": "The sky is the limit \u00A9 {}",
"banner": "This is a automatically generated application. Remove this banner once your application is ready.",
"drawer_item": {
- "about": "About"
+ "about": "About",
+ "widgets": "Widgets"
}
}
}
diff --git a/sepal_ui/templates/panel_app/component/tile/widget_tile.py b/sepal_ui/templates/panel_app/component/tile/widget_tile.py
new file mode 100644
index 00000000..612a7751
--- /dev/null
+++ b/sepal_ui/templates/panel_app/component/tile/widget_tile.py
@@ -0,0 +1,96 @@
+"""A demo tile showing some of the most frequent widgets available in the sepal_ui library."""
+
+import ipyvuetify as v
+import sepal_ui.sepalwidgets as sw
+from sepal_ui.mapping.sepal_map import SepalMap
+
+
+class WidgetTile(sw.Card):
+ def __init__(self):
+ """A demo tile showing all the widgets available in the sepal_ui library."""
+ # create the card
+ super().__init__(row=True, wrap=True, _metadata={"mount_id": "widget_tile"})
+
+ # Create widgets
+
+ select = sw.Select(
+ label="Select",
+ v_model="option1",
+ items=["option1", "option2", "option3"],
+ outlined=True,
+ )
+
+ slider = sw.Slider(
+ label="Slider",
+ v_model=50,
+ max=100,
+ min=0,
+ thumb_label=True,
+ step=1,
+ )
+
+ btn = sw.Btn("Button", color="primary", outlined=True)
+ btn1 = sw.Btn("Button", color="secondary", outlined=True)
+ btn2 = sw.Btn("Button", color="primary")
+ btn3 = sw.Btn("Button", color="secondary")
+ file_input = sw.FileInput()
+ map_ = SepalMap(zoom=10)
+
+ title = v.CardTitle(children=["test widgets tile"])
+
+ card = v.Card(class_="mb-4 pa-2", raised=True, xs12=True, children=[title, map_])
+
+ loading_card = v.Card(
+ class_="mb-6 pa-2",
+ raised=True,
+ xs12=True,
+ children=[title] + ["content " * 100],
+ loading=True,
+ )
+
+ warning_alert = sw.Alert().add_msg("This is a warning alert", type_="warning")
+ error_alert = sw.Alert().add_msg("This is an error alert", type_="error")
+ info_alert = sw.Alert().add_msg("This is an info alert", type_="info")
+ success_alert = sw.Alert().add_msg("This is a success alert", type_="success")
+
+ # Add widgets to the layout
+
+ self.children = [
+ card,
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[select]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[file_input]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[slider]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[btn]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[btn1]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[btn2]),
+ v.Flex(class_="mb-2", xs12=True, lg6=True, xl4=True, children=[btn3]),
+ v.Flex(
+ class_="mb-2",
+ xs12=True,
+ lg6=True,
+ xl4=True,
+ children=[warning_alert],
+ ),
+ v.Flex(
+ class_="mb-2",
+ xs12=True,
+ lg6=True,
+ xl4=True,
+ children=[error_alert],
+ ),
+ v.Flex(
+ class_="mb-2",
+ xs12=True,
+ lg6=True,
+ xl4=True,
+ children=[info_alert],
+ ),
+ v.Flex(
+ class_="mb-2",
+ xs12=True,
+ lg6=True,
+ xl4=True,
+ children=[success_alert],
+ ),
+ loading_card,
+ ]
diff --git a/sepal_ui/templates/panel_app/ui.ipynb b/sepal_ui/templates/panel_app/ui.ipynb
index 67515fed..07a4358f 100644
--- a/sepal_ui/templates/panel_app/ui.ipynb
+++ b/sepal_ui/templates/panel_app/ui.ipynb
@@ -14,6 +14,7 @@
"outputs": [],
"source": [
"from sepal_ui import sepalwidgets as sw\n",
+ "from component.tile.widget_tile import WidgetTile\n",
"from component.message import cm"
]
},
@@ -45,9 +46,9 @@
"source": [
"# load the partial files\n",
"%run \"about_ui.ipynb\"\n",
- "\n",
+ "widget_tile = WidgetTile()\n",
"# Gather all the partial tiles that you created previously\n",
- "app_content = [about_tile, disclaimer_tile]"
+ "app_content = [about_tile, disclaimer_tile, widget_tile]"
]
},
{
@@ -60,6 +61,7 @@
"# fmt: off\n",
"items = [\n",
" sw.DrawerItem(cm.app.drawer_item.about, \"fa-solid fa-question-circle\", card=\"about_tile\"),\n",
+ " sw.DrawerItem(cm.app.drawer_item.widgets, \"fa-solid fa-wrench\", card=\"widget_tile\"),\n",
"]\n",
"# fmt: on\n",
"\n",
@@ -115,7 +117,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.10"
+ "version": "3.10.12"
},
"voila": {
"theme": "dark"
diff --git a/tests/test_frontend/test_Frontend.py b/tests/test_frontend/test_Frontend.py
index 322132df..e1ec4391 100644
--- a/tests/test_frontend/test_Frontend.py
+++ b/tests/test_frontend/test_Frontend.py
@@ -40,26 +40,26 @@ def test_conf() -> None:
def test_repr_html() -> None:
"""Check the html representation of a color object."""
# Arrange
- expected_title_dark = "Current theme: dark
"
- expected_dark = "primary#b3842e"
-
# select dark theme
color = SepalColor()
color._dark_theme = True
+ expected_title_dark = "Current theme: dark
"
+ expected_dark = f"primary{color.primary}"
+
# read the html result and assert that they look like expected
html = color._repr_html_().__str__()
html = re.sub(r"[\n] [ ]+", "", html)
assert expected_title_dark in html
assert expected_dark in html
- # same for light theme
- expected_title_light = "Current theme: light
"
- expected_light = "primary#1976D2"
-
# select light theme
color._dark_theme = False
+ # same for light theme
+ expected_title_light = "Current theme: light
"
+ expected_light = f"primary{color.primary}"
+
# read html and assert the values of some produced items
html = color._repr_html_().__str__()
html = re.sub(r"[\n] [ ]+", "", html)
diff --git a/tests/test_mapping/test_MenuControl.py b/tests/test_mapping/test_MenuControl.py
index b55c6c40..071cafb4 100644
--- a/tests/test_mapping/test_MenuControl.py
+++ b/tests/test_mapping/test_MenuControl.py
@@ -1,6 +1,5 @@
"""Test the Menu Control."""
-from sepal_ui import color
from sepal_ui import mapping as sm
from sepal_ui import sepalwidgets as sw
@@ -135,6 +134,6 @@ def test_activate() -> None:
# close the controls
tile_control.menu.v_model = False
- assert btn.style_ == f"background: {color.bg};"
+ assert btn.style_ == ""
return
diff --git a/tests/test_sepalwidgets/test_StateIcon.py b/tests/test_sepalwidgets/test_StateIcon.py
index 3b6b88ad..91b6017e 100644
--- a/tests/test_sepalwidgets/test_StateIcon.py
+++ b/tests/test_sepalwidgets/test_StateIcon.py
@@ -23,7 +23,7 @@ def test_init(model: LocalModel) -> None:
# Test with default states
state_icon = sw.StateIcon(model, "state_value")
- assert state_icon.icon.color == color.success
+ assert state_icon.icon.color == "success"
assert state_icon.children[0] == "Valid"
# Test with custom states
@@ -48,7 +48,7 @@ def test_swap(model: LocalModel) -> None:
state_icon = sw.StateIcon(model, "state_value")
model.state_value = "non_valid"
- assert state_icon.icon.color == color.error
+ assert state_icon.icon.color == "error"
assert state_icon.children[0] == "Not valid"
# Test raise exception