I came across a possible bug while experimenting with the kanban demo: when items are sent to another grid via send method, their width is set to the width of the window on the receiving grid. Such item will render nicely into its receiving container looking ok until such time that it is being dragged, and that is when the faulty width is being exposed.
I tried to run receivingGrid.refreshItems().layout() in on receive and layoutReceiver, but it wouldn't correct the width. Although, when it is executed from the terminal it works, so I guess it is being run too early on on receive and layoutReceiver.
My quick solution for the time being was to save the width of the item on the sending grid and to set _width and _outerWidth of the item to that on the receiving grid in layoutReceiver. This works for now because the grids are of equal width.
Hi!
Although it may seem like it at first glance, it's most likely not a bug. It's actually very carefully thought and intended behaviour :smile: Muuri never touches the dimensions of the items, it only reads them and caches them whenever grid.refreshItems() is called. In an earlier version Muuri did set inline width and height to the items for the duration of the layout to handle this exact scenario, but in the end that was a bit of a nasty hack and in my humble opinion not Muuri's job at all, which is why I removed that behaviour.
So, to recap, if I'm understanding this correctly the issue you're facing here is that when you're sending items the width of the items change. And that's probably because the width of the items are most likely set to auto or with percentages (e.g. 100%). When you send an item to another grid there is the appendTo option which is by default document.body which literally means that the item is appended to document.body for the duration of the send procedure (layout). This also means that the item's dimensions are going to be calculated (by the browser) relative to the document.body for the duration of the layout, which is most likely not what you want happen. You probably want the item to maintain it's current width and height.
The secret sauce to overcome this issue is setting inline width and height to the item for the duration of the layout. And also note that you have to do the same kind of gimmick for drag if you have set the dragContainer to something else than null. Check the code below (haven't tested, but should workâ„¢).
var grid = new Muuri('.grid', {
dragEnabled: true,
dragContainer: document.body
})
.on('dragStart', function (item) {
item.getElement().style.width = item.getWidth() + 'px';
item.getElement().style.height = item.getHeight() + 'px';
})
.on('dragReleaseEnd', function (item) {
item.getElement().style.width = '';
item.getElement().style.height = '';
})
.on('layoutStart', function (items) {
items.forEach(function (item) {
item.getElement().style.width = item.getWidth() + 'px';
item.getElement().style.height = item.getHeight() + 'px';
});
})
.on('layoutEnd', function (items) {
items.forEach(function (item) {
item.getElement().style.width = '';
item.getElement().style.height = '';
});
});
Do note that the code above is missing edge case handling. For example if the an item is removed/hidden/dragged during layout it will still be included in the items argument of the layoutEnd event, so you probably need to check for that before removing the inline styles of the item. Also if the receiving container's width is different than the sending container's width you need to refresh items' dimensions that were moved to the new container.
Hope this helped you a bit :slightly_smiling_face:
Actually, after testing this myself, there indeed seems to be a bug. Check out this codepen demo: https://codepen.io/niklasramo/pen/yXzoLQ. Clicking the items sends it to the other grid and dragStartPredicate has a 300ms delay set to allow item click go through. Anyways, the sent item is appended to the document.body before the layoutStart event is called and it's dimensions are automatically refreshed by Muuri which means that there is currently no way to "freeze" it's dimensions before the appending procedure. This is definitely a bug.
We could fix this by adding a new event (e.g. "sendStart") which is triggered at the start of the send procedure before the appending takes place so we could set the inline width and height. How does that sound?
I'm also starting to rethink my decision to remove the automatic dimension "freezing" of item elements, because this is indeed a bit of a hassle for the user.
Thanks for the insight! I think I now have a pretty good understanding of how it works.
I was testing send with layoutDuration: 0 so I didn't even notice the send bug. However, I can reproduce it after enabling the animations. Btw, I had to disable the layout animations because handling over a hundred items per grid made the animations too sluggish.
Anyway, I was able to work my way around the send bug by setting the width style of the item to the current calculated width before sending it and then undoing the width style in the layoutReceiver. The dragging also works now as the drag* listeners are in place (the layout* listeners are not).
Below is a working example of the workaround as a jQuery click listener. In this scenario there's a send button in the item element which sends the item from grid0 to grid1.
$(document).on('click', '.send-button', function () {
var item = $(this).closest('.board-item');
item.css('width', item.width() + 'px');
muuriInstances[0].send(item[0],muuriInstances[1],0,{
layoutReceiver: function (isAborted, items) {
items[0].getElement().style.width = '';
}
});
});
Regarding your suggestion, sendStart would be and ideal vessel to do this kind of trickery. However, I do support the dimension "freezing" as the potential default in the future versions as it would probably be useful to most developers.
Thanks for the solution @argie09! I updated the codepen example also with a little hack to overcome this issue until it's fixed. Did the same trick as you, setting the width and height of the item before calling send.
By the way I also noted that the dragStart event is called after the appending happens, but it's all synchronous stuff between the append and the event so that's why it works. However, optimally there should be an event emitted before the appending happens so we can set the inline styles before the width goes all Mister Fantastic. I was thinking of adding something like dragInit event.
I gotta do some perf testing for those animations and get them running as fast as possible. I know that there's a lot of room for improvement and will try to make them as buttery smooth as possible ;)
Alright. Glad that the bugs got discovered.