Dynamically resize cards by dragging

,

I've been playing around with https://gallery.shinyapps.io/assistant/

On this website it's possible to drag to resize area of the different panes. I want to do something similar with a bslib dashboard layout.

I have been trying to get LLMs to achieve this - and it seems to be technically possible with custom css - but my results have been unsatisfactory.

I want to make this a feature request, but in the meanwhile I would also be grateful for other tips on how to achieve this.

please provide a minimal example.

This is minimal in the sense that it only uses flexbox css, but doesn't work as intended. The scaling is off and it's only possible to drag in bottom-right corner.

I think this is possibly just too difficult for me to solve. Bullying chatgpt only goes so far. But it would be a very cool feature and if there are reasonable approaches to achieve this, I'm again grateful to be pointed in right direction.

library(shiny)
library(bslib)

ui <- page_fluid(
  theme = bs_theme(),
  
  tags$head(
    tags$style(HTML("
      .card-container {
        display: flex;
        height: 100vh;
        overflow: hidden;
      }
      .bslib-card {
        flex-grow: 1;
        margin: 5px;
        padding: 20px;
        overflow: auto;
        resize: horizontal; /* Enables horizontal resizing */
        border: 1px solid #ccc;
        box-sizing: border-box;
      }
    "))
  ),
  
  div(
    class = "card-container",
    card(
      class = "bslib-card",
      full_screen = TRUE,
      title = "Card 1",
      body = "Content for card 1."
    ),
    card(
      class = "bslib-card",
      full_screen = TRUE,
      title = "Card 2",
      body = "Content for card 2."
    ),
    card(
      class = "bslib-card",
      full_screen = TRUE,
      title = "Card 3",
      body = "Content for card 3."
    )
  )
)

server <- function(input, output, session) {}

shinyApp(ui, server)

edit: here a working (but not so minimal) example of what i want to achieve

library(shiny)
library(bslib)

# Define UI
ui <- fluidPage(
  theme = bs_theme(version = 5),
  tags$head(
    tags$style(HTML("
      html, body {
        height: 100%;
        margin: 0;
        overflow: hidden;
      }
      .container {
        display: flex;
        width: 100vw;
        height: 100vh;
        overflow: hidden;
      }
      .column {
        display: flex;
        flex-direction: column;
        flex: 1;
        overflow: hidden;
      }
      .card {
        flex: 1;
        padding: 10px;
        overflow: auto;
        border: 1px solid #ddd;
      }
      .resizer-horizontal {
        width: 5px;
        cursor: col-resize;
        background-color: #ddd;
        position: relative;
        z-index: 1;
      }
      .resizer-vertical {
        height: 5px;
        cursor: row-resize;
        background-color: #ddd;
        position: relative;
        z-index: 1;
      }
    ")),
    # JavaScript to handle resizing
    tags$script(HTML("
      $(document).ready(function() {
        let isResizing = false, lastX = 0, lastY = 0;
        
        const container = $('.container');
        const leftColumn = $('.column').eq(0);
        const topCard = $('.card').eq(0);
        const bottomCard = $('.card').eq(1);
        const rightCard = $('.card').eq(2);
        const horizontalResizer = $('.resizer-horizontal');
        const verticalResizer = $('.resizer-vertical');

        // Horizontal resizing
        horizontalResizer.on('mousedown', function(e) {
          isResizing = 'horizontal';
          lastX = e.clientX;
          $(document).on('mousemove', resize);
          $(document).on('mouseup', stopResize);
        });

        // Vertical resizing
        verticalResizer.on('mousedown', function(e) {
          isResizing = 'vertical';
          lastY = e.clientY;
          $(document).on('mousemove', resize);
          $(document).on('mouseup', stopResize);
        });

        function resize(e) {
          if (!isResizing) return;
          if (isResizing === 'horizontal') {
            const offsetRight = container.width() - (e.clientX - container.offset().left);
            leftColumn.css('flex', 'none');
            leftColumn.css('width', e.clientX - container.offset().left + 'px');
            rightCard.css('width', offsetRight - horizontalResizer.width() + 'px');
          } else if (isResizing === 'vertical') {
            const offsetBottom = leftColumn.height() - (e.clientY - leftColumn.offset().top);
            topCard.css('flex', 'none');
            bottomCard.css('flex', 'none');
            topCard.css('height', e.clientY - leftColumn.offset().top + 'px');
            bottomCard.css('height', offsetBottom - verticalResizer.height() + 'px');
          }
        }

        function stopResize() {
          isResizing = false;
          $(document).off('mousemove', resize);
          $(document).off('mouseup', stopResize);
        }
      });
    "))
  ),
  div(
    class = "container",
    # Left column with two vertically resizable cards
    div(
      class = "column",
      div(class = "card bg-light", "Card 1 (Top)"),
      div(class = "resizer-vertical"),
      div(class = "card bg-light", "Card 2 (Bottom)")
    ),
    div(class = "resizer-horizontal"),
    # Right column with a single card
    div(class = "column",
        div(class = "card bg-light", "Card 3 (Right)")
    )
  )
)

# Define server logic
server <- function(input, output, session) {}

# Run the application
shinyApp(ui = ui, server = server)

edit2: i think my best bet is to abandon bslib layout and use a js package for this, like https://split.js.org/#/split-grid

its probably outside the scope for bslib package to handle