diff --git a/.gitignore b/.gitignore index b1b7904b..fa0a67c7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ webviz_core_components/**/* !webviz_core_components/__init__.py !webviz_core_components/graph.py +!webviz_core_components/flexbox.py +!webviz_core_components/_argument_modifier.py + DESCRIPTION NAMESPACE R/ diff --git a/usage.py b/usage.py index 516639bf..c6498d9d 100644 --- a/usage.py +++ b/usage.py @@ -1,20 +1,52 @@ import dash from dash.dependencies import Input, Output, State import dash_html_components as html -import webviz_core_components +import webviz_core_components as wcc app = dash.Dash(__name__) app.layout = html.Div( [ - webviz_core_components.WebvizPluginPlaceholder( - id="plugin", children=["Hello world"] + wcc.WebvizPluginPlaceholder(id="plugin", children=["Hello world"]), + wcc.WebvizPluginPlaceholder( + children=[ + wcc.FlexBox( + children=[ + html.Div( + "First element (before break)", + style={"background-color": "rgba(0, 255, 255, 0.2)"}, + ), + html.Div(style={"height": "0px", "width": "100%"}), + html.Div( + "Second", + style={ + "width": "20%", + "background-color": "rgba(255, 0, 0, 0.2)", + }, + ), + html.Div( + "Third", + style={ + "width": "40%", + "background-color": "rgba(0, 255, 0, 0.2)", + }, + ), + html.Div( + "Fourth", + style={ + "width": "40%", + "background-color": "rgba(0, 0, 255, 0.2)", + }, + ), + ] + ) + ] ), - webviz_core_components.WebvizPluginPlaceholder( + wcc.WebvizPluginPlaceholder( id="some-other-plugin", children=[ - webviz_core_components.ColorScales(id="colorscale"), - webviz_core_components.Graph( + wcc.ColorScales(id="colorscale"), + wcc.Graph( id="example-graph", figure={ "data": [ diff --git a/webviz_core_components/__init__.py b/webviz_core_components/__init__.py index a3ab41d2..77977e7f 100644 --- a/webviz_core_components/__init__.py +++ b/webviz_core_components/__init__.py @@ -14,8 +14,10 @@ from ._imports_ import __all__ from .graph import Graph + from .flexbox import FlexBox + + __all__ += ["Graph", "FlexBox"] - __all__.append("Graph") except ModuleNotFoundError: # The _imports_ file does not exist before dash-generate-components # has been called. diff --git a/webviz_core_components/_argument_modifier.py b/webviz_core_components/_argument_modifier.py new file mode 100644 index 00000000..325563c1 --- /dev/null +++ b/webviz_core_components/_argument_modifier.py @@ -0,0 +1,29 @@ +import inspect + + +def argument_modifier(parent_class, argument_name, modifying_function, args, kwargs): + """Intelligently modifies input args and kwargs given to a class __init__ function. + + * parent_class: The parent class to look for wrt. arguments. + * argument_name: The name of the argument to (potentially) change + * modifying_function: Reference to a function which should take one value. It is + automatically given the current value of the relevant argument + (None if not given), and should return the new modified value. + * args, kwargs: The arguments to be changed. + + Returns new pair of args and kwargs. + """ + + arg_index = inspect.getfullargspec(parent_class).args.index(argument_name) + + if len(args) > arg_index: # given as positional argument + args = ( + args[:arg_index] + + (modifying_function(args[config_arg_index]),) + + args[arg_index + 1 :] + ) + else: + if modifying_function(kwargs.get(argument_name)) is not None: + kwargs[argument_name] = modifying_function(kwargs.get(argument_name)) + + return args, kwargs diff --git a/webviz_core_components/flexbox.py b/webviz_core_components/flexbox.py new file mode 100644 index 00000000..3c70bdca --- /dev/null +++ b/webviz_core_components/flexbox.py @@ -0,0 +1,45 @@ +import copy + +import dash_html_components as html + +from ._argument_modifier import argument_modifier + + +class FlexBox(html.Div): + """Behaves like Div from dash_html_components, but extends that container with + flexbox style settings. It also adds necessary flex CSS settings to its children. + + Other style settings are untouched. + """ + + def __init__(self, *args, **kwargs): + def container_style(old_style): + style = {} if old_style is None else copy.deepcopy(old_style) + style.update({"display": "flex", "flexWrap": "wrap"}) + return style + + def update_children_style(children): + if children is not None: + for child in children: + style = ( + copy.deepcopy(child.style) if hasattr(child, "style") else {} + ) + + style["flex"] = style.get("flex", "auto") + + if "min-width" not in style and "minWidth" not in style: + style["minWidth"] = "250px" + + child.style = style + + return children + + args, kwargs = argument_modifier( + html.Div, "style", container_style, args, kwargs + ) + + args, kwargs = argument_modifier( + html.Div, "children", update_children_style, args, kwargs + ) + + super().__init__(*args, **kwargs) diff --git a/webviz_core_components/graph.py b/webviz_core_components/graph.py index fd5a8974..bfb4ae60 100644 --- a/webviz_core_components/graph.py +++ b/webviz_core_components/graph.py @@ -1,6 +1,8 @@ import inspect + import dash_core_components as dcc +from ._argument_modifier import argument_modifier # dash-core-components provide their own plotly javascript bundle, # which is not needed since webviz-core-components does the same @@ -14,21 +16,9 @@ class Graph(dcc.Graph): def __init__(self, *args, **kwargs): - config_arg_index = inspect.getfullargspec(dcc.Graph).args.index("config") - - if len(args) > config_arg_index: # config given as positional argument - args = ( - args[:config_arg_index] - + (Graph.populate_config(args[config_arg_index]),) - + args[config_arg_index + 1 :] - ) - - elif "config" in kwargs: # config given as keyword argument - kwargs["config"] = Graph.populate_config(kwargs["config"]) - - else: # config not given - give with only default values - kwargs["config"] = Graph.populate_config() - + args, kwargs = argument_modifier( + dcc.Graph, "config", Graph.populate_config, args, kwargs + ) super().__init__(*args, **kwargs) @staticmethod