Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ipywidgets without an extra div #291

Open
egormkn opened this issue Dec 22, 2023 · 3 comments
Open

Adding ipywidgets without an extra div #291

egormkn opened this issue Dec 22, 2023 · 3 comments

Comments

@egormkn
Copy link

egormkn commented Dec 22, 2023

Hi!
When I use ipywidgets inside of ipyvuetify widget, they are wrapped in a <div stye="height: 100%;"></div>. This doesn't allow customizing layout with flexbox, because I can't change the class or style of this wrapper. Is it possible to display ipywidgets without an extra div? It seems that when I put ipywidgets widget inside another ipywidgets widget (VBox), there is no extra div.

Example:

image

import ipywidgets as w
import ipyvuetify as v

button = w.Button(description="ipywidgets button")

label = v.Html(tag="span", children=["Example label"])

v_button = v.Btn(children=["Vue Button"])

input = w.BoundedFloatText(
    value=10,
    layout=w.Layout(width="auto", min_width="var(--jp-widgets-inline-width)", flex="1 0 100%"),
)

example = v.Html(
    tag="div",
    style_="display: flex; flex-flow: row nowrap; align-items: center;",
    children=[label, v_button, input],
)

# solara run example.py:example
@egormkn
Copy link
Author

egormkn commented Dec 25, 2023

I found the code that creates that div in ipyvue repository. Maybe this issue should be moved there.

https://github.com/widgetti/ipyvue/blob/d307dbdceaa05f01fe22be57d157d02c9ee8fbdf/js/src/VueRenderer.js#L48-L50

Can we mount a JupyterPhosphorWidget to this.$el.parentNode and either remove this.$el from DOM or hide it with dispay: none?

Also, for Vue v3 it should be possible to create a placeholder DOM node (or a comment node) instead of div, as it's done for components with multiple root nodes. However, I hope this issue can be resolved quickly without upgrading to Vue v3.

@egormkn
Copy link
Author

egormkn commented Dec 27, 2023

I tried to create a comment node in render function, mount a JupyterPhosphorWidget as a sibling to this node and then remove the comment node: widgetti/ipyvue@master...egormkn:ipyvue:placeholder-dom-node
It now renders correctly, however I'm not sure if such kind of manual DOM manipulation is safe in terms of Vue reactivity.

Also, in my tests in Jupyterlab I noticed a bug that is caused by that height: 100%: the wrapper div takes 100% of the output height.
image

Code from the screenshot
import reacton
import reacton.ipyvuetify as rv
import reacton.ipywidgets as rw


@reacton.component
def ButtonClick(clicks=0, set_clicks=lambda _: None):
    def my_click_handler(*ignore_args):
        # trigger a new render with a new value for clicks
        set_clicks(clicks+1)
    button = rv.Btn(children=[f"Clicked {clicks} times"])
    rv.use_event(button, 'click', my_click_handler)
    return button

@reacton.component
def Example():
    message, set_message = reacton.use_state("")
    clicks, set_clicks = reacton.use_state(0)

    with rv.Html(tag="div") as wrapper:
        # with rv.Html(tag="div", style_="display: flex; flex-direction: column;"):
        with rv.Html(tag="div"):
            for i in range(-clicks, 0):
                rw.Button(description=f"Click {i}", on_click=lambda *args: set_message(f"Clicked on {i}"), button_style="info")
            ButtonClick(clicks, set_clicks)
            for i in range(1, clicks + 1):
                rw.Button(description=f"Click {i}", on_click=lambda *args: set_message(f"Clicked on {i}"), button_style="info")
        if message:
            rv.Alert(children=[message], type="info")

    return wrapper

Example()

@mariobuikhuizen
Copy link
Collaborator

yeah, the extra div is indeed a pain point, but we have not found a way around it yet.

I've tried your branch and it works for getting an embedded widget in. After I removed refNode.remove(); I could also replace an embedded widget. But changing the order of widgets of the same parent doesn't work.

Notebook example:

import ipywidgets
import ipyvue as vue

children = [ipywidgets.Button(description="hi1"), ipywidgets.Button(description="hi2")]

main = vue.Html(tag="div", children=children)
main

Then change the order:

main.children = children[::-1]

Notice the order of the children doesn't change in the DOM.

A workaround for the extra div is to have a div with the right properties in ipyvue and add the embeded widget to that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants