Skip to content

Commit

Permalink
Update code to match the one in drag-and-drop.md
Browse files Browse the repository at this point in the history
Update Phoenix app code to match the one describe in the markdown
documentation
  • Loading branch information
SimonLab committed Nov 2, 2022
1 parent 5bd6e18 commit 3bffb9b
Show file tree
Hide file tree
Showing 18 changed files with 253 additions and 301 deletions.
79 changes: 56 additions & 23 deletions assets/css/app.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

/* This file is for your main application CSS */
@import "./phoenix.css";

/* Alerts and form errors used by phx.new */
.alert {
Expand Down Expand Up @@ -53,47 +50,83 @@
cursor: wait;
}


.select-wrapper select {
@apply text-sm border-gray-300 rounded-md shadow-sm disabled:bg-gray-100 disabled:cursor-not-allowed focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:focus:border-primary-500 dark:bg-gray-800 dark:text-gray-300 focus:outline-none ;
}

label.has-error:not(.phx-no-feedback) {
@apply !text-red-900 dark:!text-red-200;
.phx-modal {
opacity: 1!important;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
}

textarea.has-error:not(.phx-no-feedback), input.has-error:not(.phx-no-feedback), select.has-error:not(.phx-no-feedback) {
@apply !border-red-500 focus:!border-red-500 !text-red-900 !placeholder-red-700 !bg-red-50 dark:!text-red-100 dark:!placeholder-red-300 dark:!bg-red-900 focus:!ring-red-500;
.phx-modal-content {
background-color: #fefefe;
margin: 15vh auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}

input[type=file_input].has-error:not(.phx-no-feedback) {
@apply !border-red-500 !rounded-md focus:!border-red-500 !text-red-900 !placeholder-red-700 !bg-red-50 file:!border-none dark:!border-none dark:!bg-[#160B0B] dark:text-red-400;
.phx-modal-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}

input[type=checkbox].has-error:not(.phx-no-feedback) {
@apply !border-red-500 !text-red-900 dark:!text-red-200;
.phx-modal-close:hover,
.phx-modal-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}

input[type=radio].has-error:not(.phx-no-feedback) {
@apply !border-red-500;
.fade-in-scale {
animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys;
}

/* Modal animation */
.animate-fade-in-scale {
animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys;
.fade-out-scale {
animation: 0.2s ease-out 0s normal forwards 1 fade-out-scale-keys;
}

.animate-fade-in {
.fade-in {
animation: 0.2s ease-out 0s normal forwards 1 fade-in-keys;
}
.fade-out {
animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys;
}

@keyframes fade-in-scale-keys{
0% { scale: 0.95; opacity: 0; }
100% { scale: 1.0; opacity: 1; }
}

@keyframes fade-out-scale-keys{
0% { scale: 1.0; opacity: 1; }
100% { scale: 0.95; opacity: 0; }
}

@keyframes fade-in-keys{
0% { opacity: 0; }
100% { opacity: 1; }
}

@keyframes fade-out-keys{
0% { opacity: 1; }
100% { opacity: 0; }
}

.cursor-grab{
cursor: grab;
}

.cursor-grabbing{
cursor: grabbing;
}

.bg-yellow-300{
background-color: rgb(253 224 71);
}
118 changes: 58 additions & 60 deletions assets/js/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// We import the CSS which is extracted to its own file by esbuild.
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
import "../css/app.css"

// If you want to use Phoenix channels, run `mix help phx.gen.channeItem 2l`
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
// to get started and then uncomment the line below.
// import "./user_socket.js"

Expand All @@ -25,88 +26,86 @@ import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"

let Hooks = {}
Hooks.SortList = {
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")

let Hooks = {};
Hooks.Items = {
mounted() {
const hook = this
this.el.addEventListener("sortListEvent", e => {
// get list of ids in the new order
const itemIds = [...document.querySelectorAll('.draggable')].map(e => e.dataset.id)
hook.pushEventTo("#items", "sort-items", {itemIds: itemIds})

this.el.addEventListener("highlight", e => {
hook.pushEventTo("#items", "highlight", {id: e.detail.id})
})

this.el.addEventListener("hightlightItem", e => {
itemId = e.detail.id
hook.pushEventTo("#items", "highlight-item", {itemId: itemId})
this.el.addEventListener("remove-highlight", e => {
hook.pushEventTo("#items", "remove-highlight", {id: e.detail.id})
})

this.el.addEventListener("removeHighlight", e => {
itemId = e.detail.id
hook.pushEventTo("#items", "remove-highlight", {itemId: itemId})

this.el.addEventListener("dragoverItem", e => {
const currentItemId = e.detail.currentItemId
const selectedItemId = e.detail.selectedItemId
if( currentItemId != selectedItemId) {
hook.pushEventTo("#items", "dragoverItem", {currentItemId: currentItemId, selectedItemId: selectedItemId})
}
})


this.el.addEventListener("dragElt", e => {
idOver = e.detail.idOver
idDragged = e.detail.idDragged
// hook.pushEventTo("#items", "drag-elt", {idOver: idOver, idDragged: idDragged})
if (idOver != idDragged) {
hook.pushEventTo("#items", "drag-elt", {idOver: idOver, idDragged: idDragged})
}

this.el.addEventListener("update-indexes", e => {
console.log('yyyyy')
const ids = [...document.querySelectorAll(".item")].map( i => i.dataset.id)
hook.pushEventTo("#items", "updateIndexes", {ids: ids})
})
}
}

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
hooks: Hooks,
dom: {
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
dom:{
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
},
params: {_csrf_token: csrfToken}
params: {_csrf_token: csrfToken}
})

window.addEventListener(`phx:highlight`, (e) => {
document.querySelectorAll(`[data-highlight]`).forEach(el => {
// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())

if(el.id == e.detail.id){
liveSocket.execJS(el, el.getAttribute("data-highlight"))
window.addEventListener("phx:highlight", (e) => {
document.querySelectorAll("[data-highlight]").forEach(el => {
if(el.id == e.detail.id) {
liveSocket.execJS(el, el.getAttribute("data-highlight"))
}
})
})

window.addEventListener(`phx:remove-highlight`, (e) => {
document.querySelectorAll(`[data-remove-highlight]`).forEach(el => {

if(el.id == e.detail.id){
liveSocket.execJS(el, el.getAttribute("data-remove-highlight"))

window.addEventListener("phx:remove-highlight", (e) => {
document.querySelectorAll("[data-highlight]").forEach(el => {
if(el.id == e.detail.id) {
liveSocket.execJS(el, el.getAttribute("data-remove-highlight"))
}
})
})

window.addEventListener(`phx:drag-and-drop`, (e) => {
overItem = document.querySelector(`#${e.detail.item_id_over}`)
draggedItem = document.querySelector(`#${e.detail.item_id_dragged}`)
const items = document.querySelector('#items')
const listItems = [...document.querySelectorAll(".draggable")]
//
if (listItems.indexOf(draggedItem) < listItems.indexOf(overItem)) {
items.insertBefore(draggedItem, overItem.nextSibling)
}
if (listItems.indexOf(draggedItem) > listItems.indexOf(overItem)) {
items.insertBefore(draggedItem, overItem)
}
})
window.addEventListener("phx:dragover-item", (e) => {
const selectedItem = document.querySelector(`#${e.detail.selected_item_id}`)
const currentItem = document.querySelector(`#${e.detail.current_item_id}`)

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
const items = document.querySelector('#items')
const listItems = [...document.querySelectorAll('.item')]

console.log(selectedItem, currentItem)

if(listItems.indexOf(selectedItem) < listItems.indexOf(currentItem)){
items.insertBefore(selectedItem, currentItem.nextSibling)
}

if(listItems.indexOf(selectedItem) > listItems.indexOf(currentItem)){
items.insertBefore(selectedItem, currentItem)
}
})

// connect if there are any LiveViews on the page
liveSocket.connect()
Expand All @@ -116,4 +115,3 @@ liveSocket.connect()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

33 changes: 0 additions & 33 deletions assets/tailwind.config.js

This file was deleted.

5 changes: 0 additions & 5 deletions assets/vendor/alpine.js

This file was deleted.

11 changes: 0 additions & 11 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,6 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

config :tailwind,
version: "3.1.8",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"
1 change: 0 additions & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ config :app, AppWeb.Endpoint,
watchers: [
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
]

# ## SSL Support
Expand Down
2 changes: 1 addition & 1 deletion drag-and-drop.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ end
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.
to add and remove the background colour.
In `assets/js/app.js` add (for example above `liveSocket.connect()`)the event listeners:
Expand Down
35 changes: 16 additions & 19 deletions lib/app/tasks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ defmodule App.Tasks do
alias App.Tasks.Item
alias Phoenix.PubSub

# PubSub functions

def subscribe() do
PubSub.subscribe(App.PubSub, "liveview_items")
end
Expand Down Expand Up @@ -120,30 +118,29 @@ defmodule App.Tasks do
Item.changeset(item, attrs)
end

def update_indexes(item_ids) do
item_ids
|> Enum.with_index(fn id, index ->
item = get_item!(id)
update_item(item, %{index: index + 1})
end)

{:ok, item_ids}
|> notify(:item_created)
end

def item_selected(item_id) do
PubSub.broadcast(App.PubSub, "liveview_items", {:item_selected, item_id})
def drag_item(item_id) do
PubSub.broadcast(App.PubSub, "liveview_items", {:drag_item, item_id})
end

def item_dropped(item_id) do
PubSub.broadcast(App.PubSub, "liveview_items", {:item_dropped, item_id})
def drop_item(item_id) do
PubSub.broadcast(App.PubSub, "liveview_items", {:drop_item, item_id})
end

def drag_and_drop(item_id_over, item_id_dragged) do
def dragover_item(current_item_id, selected_item_id) do
PubSub.broadcast(
App.PubSub,
"liveview_items",
{:drag_and_drop, {item_id_over, item_id_dragged}}
{:dragover_item, {current_item_id, selected_item_id}}
)
end

def update_items_index(ids) do
ids
|> Enum.with_index(fn id, index ->
item = get_item!(id)
update_item(item, %{index: index + 1})
end)

PubSub.broadcast(App.PubSub, "liveview_items", :indexes_updated)
end
end
Loading

0 comments on commit 3bffb9b

Please sign in to comment.