5

I am trying to create this behavior and not sure whether Gridstack supports it or not. I have 3 Gridstack grids: Grid1, Grid2, and Grid3. Grid1 is a standalone grid and Grid3 is nested inside Grid2. I need to be able to drag widgets from Grid1 both into Grid2 (outer grid) and into Grid3 (nested grid). Following samples I was able to drag widgets between 2 top level grids and create a nested grid, but not combining these 2 together. If this is supported - any pointers are appreciated.

NB: Expand the snippet to full screen

$(document).ready(function() {
  $('.grid-stack').gridstack();
});
.grid-stack {
  background: lightgoldenrodyellow;
}

.grid-stack-item-content {
  color: #2c3e50;
  text-align: center;
  background-color: #18bc9c;
}

.grid-stack .grid-stack {
  /*margin: 0 -10px;*/
  background: rgba(255, 255, 255, 0.3);
}

.grid-stack .grid-stack .grid-stack-item-content {
  background: lightpink;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.min.css" />


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.min.js'></script>
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.jQueryUI.min.js'></script>


<div class="container-fluid">
  <h1> Multilevel Nested grids demo</h1>
  <div class="grid-stack" id="container-stack">
    <div class="grid-stack-item" data-gs-x="0" data-gs-y="0" data-gs-width="4" data-gs-height="4">
      <div class="grid-stack-item-content">
        <span>Grid One</span>
        <div class="grid-stack" id="grid-one">
          <div class="grid-stack-item widget" data-gs-x="0" data-gs-y="0" data-gs-width="3" data-gs-height="1">
            <div class="grid-stack-item-content">1</div>
          </div>
          <div class="grid-stack-item widget" data-gs-x="3" data-gs-y="0" data-gs-width="3" data-gs-height="1">
            <div class="grid-stack-item-content">2</div>
          </div>
        </div>
      </div>
    </div>
    <div class="grid-stack-item" data-gs-x="4" data-gs-y="0" data-gs-width="8" data-gs-height="4">
      <div class="grid-stack-item-content">
        <span>Grid Two</span>
        <div class="grid-stack" id="grid-two">
          <div class="grid-stack-item widget" data-gs-x="0" data-gs-y="0" data-gs-width="3" data-gs-height="1">
            <div class="grid-stack-item-content">3</div>
          </div>
          <div class="grid-stack-item widget" data-gs-x="3" data-gs-y="0" data-gs-width="3" data-gs-height="1">
            <div class="grid-stack-item-content">4</div>
          </div>
          <div class="grid-stack-item" data-gs-x="6" data-gs-y="0" data-gs-width="6" data-gs-height="3">
            <div class="grid-stack-item-content">
              <span>Grid Three</span>
              <div class="grid-stack" id="grid-three">
                <div class="grid-stack-item widget" data-gs-x="0" data-gs-y="0" data-gs-width="6" data-gs-height="1">
                  <div class="grid-stack-item-content">5</div>
                </div>
                <div class="grid-stack-item widget" data-gs-x="6" data-gs-y="0" data-gs-width="6" data-gs-height="1">
                  <div class="grid-stack-item-content">6</div>
                </div>
              </div>
            </div>
          </div>

        </div>

      </div>
    </div>
  </div>
</div>
Towkir
  • 3,889
  • 2
  • 22
  • 41
Yuriy Galanter
  • 38,833
  • 15
  • 69
  • 136
  • could you add some sample code of what you have tried? – Rachel Gallen Apr 25 '19 at 21:23
  • @RachelGallen I pretty much downloaded and played around with these 2 examples: 1 - dragging widget from grid to grid: http://gridstackjs.com/demo/two.html and 2 - nested grid: http://gridstackjs.com/demo/nested.html. But I couldn't make these 2 pieces work together. – Yuriy Galanter Apr 26 '19 at 01:18
  • From what I could see from the demo, the library only seems to support rearranging of tiles, but you can't drop one inside another or anything (like a small inside a large) , they're not allboth draggable and droppable areas. I didn't look at the nested example, I didn't see it. You could probably adjust the grid stack J's to make them both drag and drop if that's what you wanted. I saw where it was set in the js last night. – Rachel Gallen Apr 26 '19 at 08:55
  • Dunno about between separate grids. I suppose why not if you can nest them. You could make one big grid container that had a grid and a the 2 nested grids in it... Always worth a shot.. – Rachel Gallen Apr 26 '19 at 08:57
  • Can I ask what is is that you want to achieve here, like what will this be used for? You might be able to achieve an 'intersect' more easily by just using jquery ui on its own and writing your own drag/drop js ? – Rachel Gallen Apr 26 '19 at 10:32
  • @RachelGallen UI I am working on has these specs where user should be able to build their own page by dragging widgets onto it and arranging them to their liking. One type of widget they can drag in is a "container widget" which, when expanded could hold widgets of its own (this only 1 level deep, no multi-hierarchy). We have been using gridstack for a while and won't be able to easily switch to another tool. And while, yes, there are other ways to achieve this - the most intuitive one for the user would be to drag widgets both into main page and into "container" widget. – Yuriy Galanter Apr 26 '19 at 13:18
  • I see what you mean. I'd have to look into gridstack a bit more. From ui/ux point of view though , I meant the same appearance could probably be achieved using jquery + jquery ui . I'll have a look if I have time. A fiddle would help... – Rachel Gallen Apr 26 '19 at 14:06
  • 1
    Thanks, I will see if I can put on fiddle snippets I've been experimenting with. And thanks for the suggestions. You're right I might have to go outside of gridstack API to something more generic. – Yuriy Galanter Apr 26 '19 at 14:12
  • @YuriyGalanter did you find any substitute ? or could you share your code in a fiddle ? – Towkir Apr 28 '19 at 16:26
  • 1
    @Towkir still tweaking around with various approaches, but the more I look into this the more it seems it will be a hack if I want to at least emulate the behavior. E.g. for the duration of the drop take inner grid out of the widget (DOM-wise) and attach it to the body of the document (while keeping its location in the same place). – Yuriy Galanter Apr 28 '19 at 20:04
  • hey @YuriyGalanter do you need the grid 3 to be draggable and resizable inside grid 2 ? just like an widget ? or it stays fixed somewhere with a fixed size? – Towkir Apr 29 '19 at 17:17
  • @Towkir ideally it should behave like a regular widget within grid 2, but if it needs to be frozen for the duration of drop (e.g. "edit" button that freezes it so user can drag widget inside) I think that should work too. – Yuriy Galanter Apr 30 '19 at 02:24

3 Answers3

4

Following samples I was able to drag widgets between 2 top level grids and create a nested grid, but not combining these 2 together. If this is supported

Sad but true, that's not possible. At least, not with gridstack.

The key behind this dragging mechanism is accomplished by the acceptWidgets option. But this can't handle multilevel .grid-stack element. Hence, error appears.

You can try to modify the script added in the snippet to something like this:

$("#container-stack").gridstack({
   acceptWidgets: '.grid-stack-item'
})

But sadly, this will cause error once you start dragging any widget. But it works with single level nesting, which is not definitely what you are looking for.

The documentation also does not indicate anything at all regarding the nested level dragging.

But the reason I am assuming this isn't possible is this issue, also this one.

I guess this is (almost) exactly what you wanted. but there is no response from any officials. Also, it's three years old. Another flaw that indicates this project is dying is when you try to access some of their files such as the script, you get a security warning, which prevented me to add this snippet for a while.


Therefore, if I were you I would go for jqueryUI and custom code this.


Update

Here is a snippet of something similar to what you expected I guess, let me know if this is right, then I will improve this once again, like adding resizing, snap to sibling widgets and a few more things:

Once again, check the snippet in fullscreen mode.

$("#gridThree").draggable({
  snap: '#gridTwo',
  snapMode: 'inner',
  zIndex: 5,
  containment: 'parent'
});

$(".widgetInOne, .widgetInTwo, .widgetInThree").draggable({
  snap: '#gridOne, #gridTwo, #gridThree',
  snapMode: 'inner',
  zIndex: false,
  stack: 'div',
  cursor: 'grab',
  // grid: [ 100, 100 ]
});

$("#gridOne, #gridTwo, #gridThree").droppable({
  accept: '.widgetInOne, .widgetInTwo, .widgetInThree',
  drop: function(event, ui) {
    if ($(event.target).find($(event.toElement)).length == 0) {
      $(event.toElement).css({
        'left': '',
        top: ''
      });
      $(event.target).append($(event.toElement));
    }
  }
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

#gridOne {
  background: #cecece;
  width: 40%;
  height: 400px;
  display: inline-block;
  margin-right: 4%;
  vertical-align: top;
}

.widgetInOne,
.widgetInTwo,
.widgetInThree {
  width: 100px;
  height: 100px;
  padding: 0.5em;
}

#gridTwo {
  background: #bfe9f3;
  width: 50%;
  height: 400px;
  display: inline-block;
  margin-left: 4%;
  vertical-align: top;
  position: relative;
}

#gridThree {
  background: #ffdda9;
  width: 300px;
  height: 300px;
  display: inline-block;
  vertical-align: top;
  position: absolute;
  right: 100px;
  top: 0;
}
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="https://jqueryui.com/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>



<div id="gridOne">
  <div class="ui-widget-content widgetInOne">
    <p>One</p>
  </div>
</div>


<div id="gridTwo">
  <div class="ui-widget-content widgetInTwo">
    <p>Two</p>
  </div>
  <div id="gridThree">
    <div class="ui-widget-content widgetInThree">
      <p>Three</p>
    </div>
  </div>
</div>

A Few Notable things here:

  • When you drag a widget into a grid, the widget element actually moves in that grid (in the DOM structure), so any parent dependent css selector might not work here. only apply css with the widget class.
  • For now the widgets only snaps to the grid inner edges (not to the outer side of other widgets), to find more check: here I couldn't find any other option but to use // grid: [ 100, 100 ] for this one.

  • Resizable option is not added here yet, hope you can tweak it as you need

Towkir
  • 3,889
  • 2
  • 22
  • 41
  • Didn't know about the cert problem on the domain, but they seem to be active on the GitHub.. Yes, I've seen the older issues, but a few versions have been released since then, so I was hoping there were some changes. I'll play around a bit with a few thoughts I have, but ultimately if nothing works I will go with your advice. – Yuriy Galanter Apr 28 '19 at 20:09
  • Right, btw, I am still trying to figure this out, somewhere, I believed this would be possible, i mean gridstack is pretty cool. Did you notice the config options in [demo two](http://gridstackjs.com/demo/two.html) ? the `removable`, and `accept-widget` these are the keys to achieve that, anyway, if I can come up with something, I will update the answer – Towkir Apr 29 '19 at 06:48
  • Hi @YuriyGalanter , no luck on that. answer updated according to my findings. Don't think it will work without breaking. – Towkir Apr 30 '19 at 09:31
  • 1
    this looks to be *very close* to behavior I need. Couple points: when "Widget 1" is dragged into inner grid - it's supposed to stick with it (move when I move it) as of now this only seems to work for "Widget 3" - I can drag it out of inner grid, then back in - and it becomes attached to it. Also, ideally widgets would exibit Gridstack behavior (snap to positions etc.) but that I think I can add later. Looks pretty cool! – Yuriy Galanter Apr 30 '19 at 20:16
  • _"Widget 1" is dragged into inner grid - it's supposed to stick with it_ that's what I am currently implementing. – Towkir May 01 '19 at 02:45
  • 1
    @YuriyGalanter check now pls, the snapping is still a bit tricky, check the _few things list_ I added right below the snippet. – Towkir May 01 '19 at 03:23
  • Ok, I think we can work with this. I am going to accept the answer, and work with this code as a starter, but please feel free if you think anything can be added to it. Thanks for your help! – Yuriy Galanter May 01 '19 at 15:57
  • 1
    Sure, Thanks !!. BTW, there is a small UX limitation which I think could be improved, which is: when you drop a widget in another grid, the widget gets to a static position, not where you drop it. this is because jQuery draggable uses relative position to achieve this, not absolute position. I was planning to modify this, but I realised it would only break more things. – Towkir May 01 '19 at 16:05
3

This is code from an old drag/drop fiddle I composed (an age ago!), with areas that are both draggable and droppable. The items that can be pulled across to either of the droppable areas can be dragged again from one area to another and dragged up and down. While the items are not sized the same as are in your example, it goes to show that the same user experience can be achieved by using simply jquery/jquery-ui without sticking to gridstack. You may save yourself some laborious hours by working outside the grid! ;)

Hope this helps

fiddle

$("#launchPad").height($(window).height() - 20);
var dropSpace = $(window).width() - $("#launchPad").width();
$("#dropZone").width(dropSpace - 70);
$("#dropZone").height($("#launchPad").height());

$(".card").draggable({
  appendTo: "#launchPad",
  cursor: "move",
  helper: 'clone',
  revert: "invalid",

});

$("#launchPad").droppable({
  tolerance: "intersect",
  accept: ".card",
  activeClass: "ui-state-default",
  hoverClass: "ui-state-hover",
  drop: function(event, ui) {
    $("#launchPad").append($(ui.draggable));
  }
});

$(".stackDrop1").droppable({
  tolerance: "intersect",
  accept: ".card",
  activeClass: "ui-state-default",
  hoverClass: "ui-state-hover",
  drop: function(event, ui) {
    $(this).append($(ui.draggable));
  }
});

$(".stackDrop2").droppable({
  tolerance: "intersect",
  accept: ".card",
  activeClass: "ui-state-default",
  hoverClass: "ui-state-hover",
  drop: function(event, ui) {
    $(this).append($(ui.draggable));
  }
});
body {
  margin: 0;
}

#launchPad {
  width: 200px;
  float: left;
  border: 1px solid #eaeaea;
  background-color: #f5f5f5;
}

#dropZone {
  float: right;
  border: 1px solid #eaeaea;
  background-color: #ffffcc;
}

.card {
  width: 150px;
  padding: 5px 10px;
  margin: 5px;
  border: 1px solid #ccc;
  background-color: #eaeaea;
}

.stack {
  width: 180px;
  border: 1px solid #ccc;
  background-color: #f5f5f5;
  margin: 20px;
}

.stackHdr {
  background-color: #eaeaea;
  border: 1px solid #fff;
  padding: 5px
}

.stackDrop1,
.stackDrop2 {
  min-height: 100px;
  padding: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>




<body>
  <div id="launchPad">
    <div class="card draggable">
      apple
    </div>
    <div class="card draggable">
      orange
    </div>
    <div class="card draggable">
      banana
    </div>
    <div class="card draggable">
      car
    </div>
    <div class="card draggable">
      bus
    </div>
  </div>

  <div id="dropZone">
    <div class="stack">
      <div class="stackHdr">
        Drop here
      </div>
      <div class="stackDrop1 droppable">

      </div>
    </div>

    <div class="stack">
      <div class="stackHdr">
        Or here
      </div>
      <div class="stackDrop2 droppable">

      </div>
    </div>
  </div>
Rachel Gallen
  • 27,943
  • 21
  • 72
  • 81
  • 1
    here's another old fiddle i found - it has [boxes](https://jsfiddle.net/RachGal/6ev9qhu0/) – Rachel Gallen Apr 30 '19 at 12:55
  • 1
    That looks really promising, thanks! Let me evaluate this, but I think this is as close to the answer we need as it gets. – Yuriy Galanter Apr 30 '19 at 14:39
  • BTW @YuriyGalanter didn't you mean `grid3` inside `grid2` ? I mean `grid3` itself can be dragged around inside `grid2` and can also contain widgets ? – Towkir Apr 30 '19 at 15:13
0

I create fork which solves the problem of nested grids.

Vitaliy Demchuk
  • 180
  • 1
  • 1
  • 9