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.