I was looking at Muuri today and trying to get the Kanban example working. Examples | Muuri Docs It took me some time to turn the CSS into tailwind and break the HTML into small functions so I am pasting what I did here. Hopefully it helps somebody else get started with Muuri + Phoenix.
defmodule MyAppWeb.MuuriLive.Kanban do
@moduledoc false
use MyAppWeb, :html
def kanban(assigns) do
~H"""
<div id="muuri-kanban-demo" phx-hook="KanbanHook">
<div class={["drag-container", "fixed top-0 left-0", "z-[1000]"]}></div>
<div class={["board", "relative"]}>
<.board_column header="Todo" indexes={1..5} />
<.board_column header="Working" indexes={6..10} />
<.board_column header="Done" indexes={11..15} />
</div>
</div>
"""
end
attr :header, :string
attr :indexes, :integer
defp board_column(assigns) do
~H"""
<div class={[
"board-column done",
"absolute top-0 left-0",
"py-0 px-[10px]",
"w-1/3",
"z-[1] muuri-item-releasing:z-[2] muuri-item-dragging:z-[3] muuri-item-dragging:cursor-move"
]}>
<div class={[
"board-column-container",
"w-full h-full"
]}>
<div class={[
"board-column-header",
"relative",
"h-[50px] leading-[50px]",
"overflow-hidden",
"py-0 px-[20px]",
"text-center",
"bg-green-500",
"text-white",
"rounded-sm",
"font-bold",
"tracking-[0.5px]",
"uppercase"
]}>
<%= @header %>
</div>
<div class={[
"board-column-content-wrapper",
"relative",
"p-[8px]",
"bg-blue-700",
"h-screen",
"overflow-y-auto",
"rounded-sm"
]}>
<div class={["board-column-content", "relative min-h-full"]}>
<.board_item :for={index <- @indexes} index={index} />
</div>
</div>
</div>
</div>
"""
end
attr :index, :integer
defp board_item(assigns) do
~H"""
<div class={[
"board-item",
"absolute w-full m-[8px]",
"muuri-item-releasing:z-[9998] muuri-item-dragging:z-[9999]",
"muuri-item-dragging:cursor-move",
"muuri-item-hidden:z-[0]"
]}>
<div class={[
"board-item-content",
"relative p-[20px]",
"bg-white",
"rounded-[4px]",
"text-[17px]",
"cursor-pointer"
]}>
<span>Item #</span><%= @index %>
</div>
</div>
"""
end
end
and the hook
import Muuri from "../../vendor/muuri";
const KanbanHook = {
mounted() {
var dragContainer = document.querySelector(".drag-container");
var itemContainers = [].slice.call(
document.querySelectorAll(".board-column-content"),
);
var columnGrids = [];
var boardGrid;
// Init the column grids so we can drag those items around.
itemContainers.forEach(function (container) {
var grid = new Muuri(container, {
items: ".board-item",
dragEnabled: true,
dragSort: function () {
return columnGrids;
},
dragContainer: dragContainer,
dragAutoScroll: {
targets: (item) => {
return [
{ element: window, priority: 0 },
{ element: item.getGrid().getElement().parentNode, priority: 1 },
];
},
},
})
.on("dragInit", 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 = "";
item.getGrid().refreshItems([item]);
})
.on("layoutStart", function () {
boardGrid.refreshItems().layout();
});
columnGrids.push(grid);
});
// Init board grid so we can drag those columns around.
boardGrid = new Muuri(".board", {
dragEnabled: true,
dragHandle: ".board-column-header",
});
},
};
export { KanbanHook };
I will be changing the JS to use this.el. For now, it is identical to the JS given in their example.
It works nice. It “feels” better than sortable, which was my go-to for drag-n-drop
The tailwind modifiers I added like this:
plugin(({ addVariant }) => addVariant("muuri", [".muuri&", ".muuri &"])),
plugin(({ addVariant }) => addVariant("muuri", [".muuri&", ".muuri &"])),
plugin(({ addVariant }) =>
addVariant("muuri-item", [".muuri-item&", ".muuri-item &"]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-shown", [
".muuri-item-shown&",
".muuri-item-shown &",
]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-hidden", [
".muuri-item-hidden&",
".muuri-item-hidden &",
]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-positioning", [
".muuri-item-positioning&",
".muuri-item-positioning &",
]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-dragging", [
".muuri-item-dragging&",
".muuri-item-dragging &",
]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-releasing", [
".muuri-item-releasing&",
".muuri-item-releasing &",
]),
),
plugin(({ addVariant }) =>
addVariant("muuri-item-placeholder", [
".muuri-item-placeholder&",
".muuri-item-placeholder &",
]),
),
in tailwind config js.