haltu / muuri

Infinite responsive, sortable, filterable and draggable layouts
https://muuri.dev
MIT License
10.77k stars 643 forks source link

How to keep a specific item of a grid frozen in the same position while the grid is being sorted from a dragSort? #567

Closed tlietz closed 5 months ago

tlietz commented 5 months ago

My goal is to have a UI that looks like the kanban board from muuri.dev, but having some elements be frozen in place when sorting happens. For example, if the 2 item was frozen, when other items are dragged, and the grid is sorted, 2 would remain in the same position.

Initial grid state: image

After dragging 1 to the bottom: image

I was trying to do this by calling grid.sort() with a custom comparer, then manually calling that sort in dragSortPredicate. However, the callback assigned to dragSortPredicate is called even when nothing is being sorted. So it seems like the location of items will have to be kept track of outside of Muuri to accomplish this.

I have also tried calling the custom grid.sort() on a layoutStart() event, but it leads to an infinite recursive loop.

I was wondering if there was a simpler/better way. Similar to how disabling an element from being dragged is done by simply returning false in dragStartPredicate.

tlietz commented 5 months ago

It doesn't seem like there is a way to accomplish the frozen functionality with the native API.

However, changing grid._items when a layoutStart event is emitted from the grid changes the resultant layout. For example, this swaps the first two items of the grid whenever an item is dragged and a sort should occur:

grid.on('layoutStart', function(items, isInstant) {
    [grid._items[0], grid._items[1]] = [grid._items[1], grid._items[0]]
});

My idea now is to keep track of grid._items before and after every layoutStart event, take a diff, then determine whether the items that have been moved to a different index of grid._items should be put back where they belong before the sort animation occurs, based on if they are frozen.

Is there a better way to do this? I have also considered changing the source code to make it better suited for freezing items.

tlietz commented 5 months ago

Muuri is very well architected, and written. Reading the source was the most pleasurable experience I have ever had with vanilla js.

I ended up changing some of the source code, specifically the grid.move and grid.swap functions, along with a public prototype method to set the frozenIndexes safely. The changes are on this fork of muuri.

Note that this is not the most performant or ergonomic way to accomplish this. For my usecase, each grid has at max 8 items, so performance doesn't really matter.

Also, it has not been tested on 2D grids.