diff --git a/assets/js/app.js b/assets/js/app.js
index 7640a22..dcbba6f 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -117,52 +117,3 @@ liveSocket.connect()
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket
-
-// Drag and Drop JS version:
-
-// Select all the items that are draggable
-// and the list of items where we can move an item to.
-//
-// const draggables = document.querySelectorAll(".draggable");
-// const listItems = document.querySelector("#items");
-//
-// draggables.forEach(dragable => {
-// dragable.addEventListener('dragstart', () => {
-// dragable.classList.add('bg-red-100', 'dragging')
-// });
-//
-// dragable.addEventListener('dragend', () => {
-// dragable.classList.remove('bg-red-100', 'dragging')
-// });
-// })
-//
-// listItems.addEventListener('dragover', e => {
-// e.preventDefault()
-// const dragged = document.querySelector('.dragging')
-// const overItem = getOverItem(e.clientY)
-// const moving = direction(dragged, overItem)
-// if (moving == "down") {
-// listItems.insertBefore(dragged, overItem.nextSibling)
-// }
-//
-// if (moving == "up"){
-// listItems.insertBefore(dragged, overItem)
-// }
-// })
-//
-// function getOverItem(y) {
-// const draggables = [...document.querySelectorAll(".draggable")]
-// return draggables.find( item => {
-// const box = item.getBoundingClientRect()
-// return y > box.top && y < box.bottom
-// })
-// }
-//
-// function direction(dragged, overItem) {
-// const draggables = [...document.querySelectorAll(".draggable")]
-// if (draggables.indexOf(dragged) < draggables.indexOf(overItem)) {
-// return "down"
-// } else {
-// return "up"
-// }
-// }
diff --git a/drag-and-drop.md b/drag-and-drop.md
index e64abfd..c071729 100644
--- a/drag-and-drop.md
+++ b/drag-and-drop.md
@@ -210,9 +210,7 @@ Running the application, you should see a UI similar to:
![create-items](https://user-images.githubusercontent.com/6057298/199272881-0581b3f8-1e15-408b-9711-05747714a92a.png)
![list-items](https://user-images.githubusercontent.com/6057298/199272939-1343c915-df0b-4b52-a003-47d047e2c6a3.png)
-
-
-### PubSub
+## Make it real time
[PubSub](https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html) is used
to send and listen to `messages`. Any clients connected to a `topic` can
@@ -320,8 +318,24 @@ See the [Phoenix LiveView JavaScript interoperability documentation](https://hex
![Alpine.js](https://user-images.githubusercontent.com/6057298/199215481-489e71fb-9a95-4d24-9484-e90b6257211c.png)
-Now we're going to start by adding a new background colour to the item being
-dragged and remove the colour when the drag ends.
+Add the following content at the end of the `assets/css/app.css` file:
+
+```css
+.cursor-grab{
+ cursor: grab;
+}
+
+.cursor-grabbing{
+ cursor: grabbing;
+}
+
+.bg-yellow-300{
+ background-color: rgb(253 224 71);
+}
+```
+
+These css classes will be used to make our items a bit more visible when moved.
+
We are going to define an Alpine component using the [x-data](https://alpinejs.dev/directives/data)
attribute:
@@ -332,13 +346,13 @@ provides the reactive data for that component to reference.
in `lib/app_web/live/item_live/index.html.heex`:
-```html
-
+```heex
+
<%= for item <- @items do %>
- <.tr id={"item-#{item.id}"} x-data="{}" draggable="true">
- <.td><%= item.text %>
- <.td><%= item.index %>
-
+
+ <%= item.text %> |
+ <%= item.index %> |
+
<% end %>
```
@@ -352,37 +366,41 @@ and [dragend](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drage
events:
-```html
-
+```heex
+
<%= for item <- @items do %>
- <.tr id={"item-#{item.id}"}
- draggable="true">
- x-data="{selected: false}"
- x-on:dragstart="selected = true"
- x-on:dragend="selected = false"
- x-bind:class="selected ? 'cursor-grabbing' : 'cursor-grab'"
- <.td><%= item.text %>
- <.td><%= item.index %>
-
+
+ <%= item.text %> |
+ <%= item.index %> |
+
<% end %>
```
When the `dragstart` event is triggered (i.e. an item is moved) we update the newly
-`selected` value to `true` (this value has been initalised in the `x-data` attribute).
+`selected` value define in `x-data` to `true`.
When the `dragend` event is triggered we set `selected` to false.
Finally we are using `x-bind:class` to add css class depending on the value of
`selected`. In this case we have customised the display of the cursor.
-To make is a bit more obvious which item is currently moved, we want to change
-the background colour for this item. We also want all connected clients to see
-the new background colour.
+To make the moved item a bit more obvious, we also change
+the background colour.
+
+In this step we also make sure that all connected clients can see
+the new background colour of the moved item!
Update the `tr` tag with the following:
```html
-<.tr
+ id}, socket) do
Tasks.drop_item(id)
{:noreply, socket}
-end @impl true
+end
```
The `Tasks` functions `drag_item` and `drop_item` are using PubSub to send
@@ -496,7 +515,7 @@ The LiveView will send the `highlight` and `remove-highlight` to the client.
The final step is to handle these Phoenix events with [Phoenix.LiveView.JS](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html)
to add and remove the background colour via Tailwind css class.
-In `assets/js/app.js` add the event listeners:
+In `assets/js/app.js` add (for example above `liveSocket.connect()`)the event listeners:
```javascript
window.addEventListener("phx:highlight", (e) => {
@@ -517,23 +536,24 @@ window.addEventListener("phx:remove-highlight", (e) => {
```
For each item we are checking if the id match the id linked to the drag/drop event,
-then execute the Phoenix.LiveView.JS function that we now have to define:
+then execute the Phoenix.LiveView.JS function that we now have to define back to our
+`lib/app_web/live/item_live/index.html.heex` file.
```heex
-<.tr
+
```
-Note the call to `add_class` and `remove_class`. You might need to add
-`alias Phoenix.LiveView.JS` in `lib/app_web/live/item_live/index.ex` to make
-sure the two functions are accessible in the template.
+To the call to `add_class` and `remove_class`, you need to add
+`alias Phoenix.LiveView.JS` at the top of the file `lib/app_web/live/item_live/index.ex`
+This alias will make sure the two functions are accessible in the liveView template.
Again there are a few steps to make sure the highlight for the selected item
@@ -553,22 +573,26 @@ event for this:
```heex
<%= for item <- @items do %>
- <.tr
+
```
We have added `x-data="{selectedItem: null}` to the `tbody` html tag.
This value represents which element is currently being moved.
+We have also added the `class="item"`. This will be used later on in `app.js`
+to get the list of items using `querySelectorAll`.
+
Then we have
`x-on:dragover.throttle="$dispatch('dragoverItem', {selectedItemId: selectedItem.id, currentItemId: $el.id})"`
@@ -607,7 +631,10 @@ def handle_event(
Tasks.dragover_item(current_item_id, selected_item_id)
{:noreply, socket}
end
+```
+and
+```
@impl true
def handle_info({:dragover_item, {current_item_id, selected_item_id}}, socket) do
{:noreply,
@@ -656,24 +683,25 @@ indexes of the items yet.
We want to send a new event when the `dragend` is emitted:
```heex
-<.tr
+
```
-We have added the `data-id` attribute to store the item's id.
+We have added the `data-id` attribute to store the item's id and created the
+`$dispatch('update-indexes')` event.
-In `app.js` we listen to the event:
+In `app.js` we listen to the event in the Hook:
```javascript
this.el.addEventListener("update-indexes", e => {
@@ -688,8 +716,9 @@ event `updateIndexes`
In `lib/app_web/live/item_live/index.ex` we add a new `handle_event`
```elixir
+@impl true
def handle_event("updateIndexes", %{"ids" => ids}, socket) do
-( Tasks.update_items_index(ids)
+ Tasks.update_items_index(ids)
{:noreply, socket}
end
```
@@ -716,6 +745,7 @@ Finally similar to the way we tell clients a new item has been created, we
broadcast a new message, `indexes_updated`:
```elixir
+@impl true
def handle_info(:indexes_updated, socket) do
items = list_items()
{:noreply, assign(socket, items: items)}
@@ -727,3 +757,6 @@ automatically.
You should now have a complete drag-and-drop feature shared with multiple
clients!
+
+Thanks for reading and again don't hesitate to open issues for questions,
+enhancement, bug fixes...