Draggable streamlit containers

Hi guys,

Today, I am sharing a new development in a project I am working on: draggable Streamlit containers! This is not like Streamlit elements in that you cannot resize or reshape the containers. It just adds the ability to drag containers (with the content inside) to a different position. Really, it only allows for re-ordering of elements on the page. Not for moving the container to arbitrary (x, y) points on the page.

Draggable-containers


Background

This past month I have taken on a personal challenge designed to test the many tricks I learned about customizing Streamlit and to incorporate some ideas that I have been itching to have a reason to implement.

The app is a Jupyter notebook environment but made in Streamlit. This is not a completely original idea as others have developed similar apps (like @B4PT0R’s really cool Streamlit Notebook).

Nonetheless, there are features that you often find in Jupyter notebook environments that do pose a challenge and require custom components and customization of Streamlit itself.

Earlier this past week I shared a package I created called streamlit-execute (post, github) that allows for python code execution on the client (user’s browser instead of backend/server).

I just got draggable containers working today and I have more planned on the horizon.

I am sharing this here to gauge peoples interest and desire for a full separate package dedicated to this functionality.

Let me know if you are interested in this being made into a package you can install and use!

14 Likes

This sounds great! It would be super cool if you could share some online demo :smiley:

2 Likes

Hi @TomJohn ,

This is something that is working but still needs tweaking and some more features added. I have it all in just 2 files at the moment. I went ahead and put it on github and published a demo.

4 Likes

Hey Anas,

Thanks for mentioning the notebook here :slight_smile:

When in doubt, go for publishing the packages… We’ll always find a way to use them!

Cheers!

1 Like

HI @B4PT0R :wave:

I appreciate your input!

The truth is that I am only one person with only so much time to spend and too many projects/packages to manage. I gauge interest to decide what to spend more time on and what to develop first. When it comes to open-source projects, I have a 1 of 3 rule where I am always working on 3 projects in parallel where 1 of the 3 is purely of interest to me (exciting to me) and the other 2 helpful/useful to others. And all of this is if time allows.

Like for this project, I have so much more to build and add like, for example, a way for the draggable containers to communicate to streamlit when the containers are moved to a new location. This way you can keep track of changes and you can prevent the containers from resetting when the browser is refreshed. Another feature, that I would like to implement is one where the drop location can be set to other containers so that we can move elements inside columns to other columns for example. There are a bunch more things and it will all take time investment.

Im happy to do all this as I enjoy building tools for other people to use, but I need some feedback as far as what interests people the most so I can direct my time to those more.

4 Likes

Hey @bouzidanas,

This looks really interesting. Do you think its possible to have them be draggable left and right as well? Into adjacet columns. Thinking it could have good use as a kanban board for teams activities, similar to that seen on GIthub projects page, moving each task into different buckets.

1 Like

Hi @charlie-turntown

Yes. It is possible. It might be some time before i can get to that. Currently, im working on allowing dragging from container to container which is working but the indicators arent working in this case…so its not really ready for use. Allowing markdown draggable elements and markdown destinations is next. And then I will be able to get it working for columns.

This is still early in development but since this has garnered some attention, I will be spending more time on it.

I would appreciate if you created a “feature request” type issue on this projects github. This helps me and others keep track of whats been requested and what to work on next.

5 Likes

Hey, is there a way to track the container that is being dragged? For example, if I have cards each uniquely identified by numeric ids (0,1,2,3 etc), will I be able to track the new position of the card and store the card that I dragged to a new id position?

1 Like

Hi,

Im still working on making this a package that you can actually use. I am in the middle of rewriting everything to make it easier to add features. One of the features i am currently working on is communication back to Streamlit which should include the destination and placement inside. Its a bit tricky because they are containers and you cant just return a dictionary.

3 Likes

Not sure if this might be of any help. When I created my custom sidebar for streamlit, I created the html via st.html and recognised the click functions via streamlit custom components.

I the custom components library, I built this:


const [value, setValue] = useState<any>(initialPage)

  const passDataToStreamlit = () => {

    var navigationTabs = window.parent.document.querySelectorAll(".custom-sidebar > .navigation-container > .navigation > .label-icon-container");

    navigationTabs.forEach((c) => {
      {

        c.addEventListener("click", (e) => {
          {

            var clickedNav = c.querySelectorAll('span')[0];
            var clickedVal = clickedNav.getAttribute('data-testid');
            setValue(clickedVal)


          }
        });
      }
    });
  }

  const passDataToStreamlitRemove = () => {

    var navigationTabs = window.parent.document.querySelectorAll(".custom-sidebar > .navigation-container > .navigation > .label-icon-container");

    navigationTabs.forEach((c) => {
      {

        c.removeEventListener("click", (e) => {
          {

            var clickedNav = c.querySelectorAll('span')[0];
            var clickedVal = clickedNav.getAttribute('data-testid');
            setValue(clickedVal)


          }
        });
      }
    });
  }

  useEffect(() => {
    Streamlit.setFrameHeight()
  })

  useEffect(() => {

    Streamlit.setComponentValue(value)
    Streamlit.setComponentReady()

    passDataToStreamlit()

    return () => {
      passDataToStreamlitRemove()
    }

  }, [value])


This was how I was able to pass data from react to streamlit even though the sidebar was built using st.html. I saved the title of the page in the variable data-testid so I am thinking maybe we can do this with the unique indexes.

So in your package, in your frontend folder, you could create a DataToStreamlit.tsx file and try something like this but for ondrag listeners.

I will test it later but its possible to send data from javascript/react to streamlit via eventListeners. The method above works for my custom sidebar.

1 Like

Interesting!

What’s different about this approach compared to postMessages like Streamlit custom components using javascript?

Not quite sure I understand this. Do you mean the difference between postMessages in javascript and Streamlit custom components?

I think they are the same. It essentially exchanges data between the backend and frontend. Data gained from typescript/react can be sent directly to streamlit via streamlit custom components. In my code above, this is done via Streamlit.setComponentValue(value)