-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PBNTR-692] Draggable in Rails (#3863)
[Runway Story](https://runway.powerhrg.com/backlog_items/PBNTR-692) - ✅ Create the subcomponent structure for draggable.option and draggable.container - ✅ Attaches reorganized list as array of string IDs to container - ✅ Use enhanced element for drag logic --------- Co-authored-by: Jasper <[email protected]> Co-authored-by: Jasper Furniss <[email protected]>
- Loading branch information
1 parent
1356072
commit ce8404d
Showing
15 changed files
with
289 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
playbook/app/pb_kits/playbook/pb_draggable/docs/_draggable_default_rails.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<% initial_items = [ | ||
{ | ||
id: "1", | ||
url: "https://unsplash.it/500/400/?image=633", | ||
}, | ||
{ | ||
id: "2", | ||
url: "https://unsplash.it/500/400/?image=634", | ||
}, | ||
{ | ||
id: "3", | ||
url: "https://unsplash.it/500/400/?image=637", | ||
}, | ||
] %> | ||
|
||
<%= pb_rails("draggable", props: {initial_items: initial_items}) do %> | ||
<%= pb_rails("draggable/draggable_container") do %> | ||
<%= pb_rails("flex") do %> | ||
<% initial_items.each do |item| %> | ||
<%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %> | ||
<%= pb_rails("image", props: { alt: item[:id], size: "md", url: item[:url], margin: "xs" }) %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> |
7 changes: 7 additions & 0 deletions
7
playbook/app/pb_kits/playbook/pb_draggable/docs/_draggable_default_rails.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
The `draggable` kit gives you a full subcomponent structure that allows it to be used with almost any kit. | ||
|
||
`initial_items` is a REQUIRED prop, which is the array of objects that contains data for the the draggable items. | ||
|
||
`draggable/draggable_container` = This specifies the container within which items can be dropped. | ||
|
||
`draggable/draggable_item` = This specifies the items that can be dragged and dropped. `drag_id` is a REQUIRED prop for draggable_item and must match the id on the items within `initial_items`. |
38 changes: 38 additions & 0 deletions
38
playbook/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<% initial_items = [ | ||
{ id: "21", name: "Joe Black" }, | ||
{ id: "22", name: "Nancy White" }, | ||
{ id: "23", name: "Bill Green" }, | ||
] %> | ||
|
||
<%= pb_rails("draggable", props: {initial_items: initial_items}) do %> | ||
<%= pb_rails("draggable/draggable_container") do %> | ||
<% initial_items.each do |item| %> | ||
<%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %> | ||
<%= pb_rails("card", props: {highlight: {position: "side", color:"primary"}, margin_bottom: "xs", padding: "xs"}) do %> | ||
<%= pb_rails("flex", props:{align_items: "stretch", flex_direction:"column"}) do %> | ||
<%= pb_rails("flex", props:{gap: "xs"}) do %> | ||
<%= pb_rails("title", props: { text: item[:name], tag: "h4", size: 4 }) %> | ||
<%= pb_rails("badge", props: {text:"35-12345" ,variant: "primary"}) %> | ||
<% end %> | ||
<%= pb_rails("caption", props: { size: "xs", text: "8:00A • Township Name • 90210" }) %> | ||
<%= pb_rails("flex", props:{gap: "xxs", spacing:"between"}) do %> | ||
<%= pb_rails("flex", props:{gap: "xxs"}) do %> | ||
<%= pb_rails("caption", props: { size: "xs" , color: "error" }) do %> | ||
<%= pb_rails("icon", props: { icon: "house-circle-exclamation", fixed_width: true }) %> | ||
<% end %> | ||
<%= pb_rails("caption", props: { size: "xs" , color: "success" }) do %> | ||
<%= pb_rails("icon", props: { icon: "file-circle-check", fixed_width: true }) %> | ||
<% end %> | ||
<% end %> | ||
<%= pb_rails("flex") do %> | ||
<%= pb_rails("badge", props: {text:"Schedule QA" ,variant: "warning", rounded: true}) %> | ||
<%= pb_rails("badge", props: {text:"Flex" ,variant: "primary", rounded: true}) %> | ||
<%= pb_rails("badge", props: {text:"R99" ,variant: "primary", rounded: true}) %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> |
Empty file.
19 changes: 19 additions & 0 deletions
19
playbook/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<% initial_items = [ | ||
{ id: "31", name: "Philadelphia" }, | ||
{ id: "32", name: "New Jersey" }, | ||
{ id: "33", name: "Maryland" }, | ||
{ id: "34", name: "Connecticut" }, | ||
|
||
] %> | ||
|
||
<%= pb_rails("draggable", props: {initial_items: initial_items}) do %> | ||
<%= pb_rails("draggable/draggable_container") do %> | ||
<%= pb_rails("list", props: {ordered: false}) do %> | ||
<% initial_items.each do |item| %> | ||
<%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %> | ||
<%= pb_rails("list/item") do %><%= item[:name] %><% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
playbook/app/pb_kits/playbook/pb_draggable/draggable.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= pb_content_tag do %> | ||
<%= content.presence %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# frozen_string_literal: true | ||
|
||
module Playbook | ||
module PbDraggable | ||
class Draggable < ::Playbook::KitBase | ||
prop :initial_items, type: Playbook::Props::Array, | ||
default: [] | ||
|
||
def data | ||
Hash(prop(:data)).merge(pb_draggable: true) | ||
end | ||
|
||
def classname | ||
generate_classname("pb_draggable") | ||
end | ||
end | ||
end | ||
end |
3 changes: 3 additions & 0 deletions
3
playbook/app/pb_kits/playbook/pb_draggable/draggable_container.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= pb_content_tag do %> | ||
<%= content.presence %> | ||
<% end %> |
15 changes: 15 additions & 0 deletions
15
playbook/app/pb_kits/playbook/pb_draggable/draggable_container.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# frozen_string_literal: true | ||
|
||
module Playbook | ||
module PbDraggable | ||
class DraggableContainer < ::Playbook::KitBase | ||
def data | ||
Hash(prop(:data)).merge(pb_draggable_container: true) | ||
end | ||
|
||
def classname | ||
generate_classname("pb_draggable_container") | ||
end | ||
end | ||
end | ||
end |
7 changes: 7 additions & 0 deletions
7
playbook/app/pb_kits/playbook/pb_draggable/draggable_item.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<%= pb_content_tag(:div, { | ||
id: "item_#{object.drag_id}", | ||
draggable: true | ||
}) do %> | ||
<%= content.presence %> | ||
<% end %> | ||
|
18 changes: 18 additions & 0 deletions
18
playbook/app/pb_kits/playbook/pb_draggable/draggable_item.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# frozen_string_literal: true | ||
|
||
module Playbook | ||
module PbDraggable | ||
class DraggableItem < ::Playbook::KitBase | ||
prop :drag_id, type: Playbook::Props::String, | ||
default: "" | ||
|
||
def data | ||
Hash(prop(:data)).merge(pb_draggable_item: true) | ||
end | ||
|
||
def classname | ||
generate_classname("pb_draggable_item") | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import PbEnhancedElement from "../pb_enhanced_element"; | ||
|
||
const DRAGGABLE_SELECTOR = "[data-pb-draggable]"; | ||
const DRAGGABLE_CONTAINER = ".pb_draggable_container"; | ||
|
||
export default class PbDraggable extends PbEnhancedElement { | ||
static get selector() { | ||
return DRAGGABLE_SELECTOR; | ||
} | ||
|
||
connect() { | ||
this.draggedItem = null; | ||
this.draggedItemId = null; | ||
document.addEventListener("DOMContentLoaded", () => this.bindEventListeners()); | ||
} | ||
|
||
bindEventListeners() { | ||
// Needed to prevent images within draggable items from being independently draggable | ||
// Needed if using Image kit in draggable items | ||
this.element.querySelectorAll(".pb_draggable_item img").forEach(img => { | ||
img.setAttribute("draggable", "false"); | ||
}); | ||
|
||
this.element.querySelectorAll(".pb_draggable_item").forEach(item => { | ||
item.addEventListener("dragstart", this.handleDragStart.bind(this)); | ||
item.addEventListener("dragend", this.handleDragEnd.bind(this)); | ||
item.addEventListener("dragenter", this.handleDragEnter.bind(this)); | ||
}); | ||
|
||
const container = this.element.querySelector(DRAGGABLE_CONTAINER); | ||
if (container) { | ||
container.addEventListener("dragover", this.handleDragOver.bind(this)); | ||
container.addEventListener("drop", this.handleDrop.bind(this)); | ||
} | ||
} | ||
|
||
handleDragStart(event) { | ||
// Needed to prevent images within draggable items from being independently draggable | ||
// Needed if using Image kit in draggable items | ||
if (event.target.tagName.toLowerCase() === 'img') { | ||
event.preventDefault(); | ||
return; | ||
} | ||
|
||
this.draggedItem = event.target; | ||
this.draggedItemId = event.target.id; | ||
event.target.classList.add("is_dragging"); | ||
|
||
if (event.dataTransfer) { | ||
event.dataTransfer.effectAllowed = 'move'; | ||
event.dataTransfer.setData('text/plain', this.draggedItemId); | ||
} | ||
|
||
setTimeout(() => { | ||
event.target.style.opacity = '0.5'; | ||
}, 0); | ||
} | ||
|
||
handleDragEnter(event) { | ||
if (!this.draggedItem || event.target === this.draggedItem) return; | ||
|
||
const targetItem = event.target.closest('.pb_draggable_item'); | ||
if (!targetItem) return; | ||
|
||
const container = targetItem.parentNode; | ||
const items = Array.from(container.children); | ||
const draggedIndex = items.indexOf(this.draggedItem); | ||
const targetIndex = items.indexOf(targetItem); | ||
|
||
if (draggedIndex > targetIndex) { | ||
container.insertBefore(this.draggedItem, targetItem); | ||
} else { | ||
container.insertBefore(this.draggedItem, targetItem.nextSibling); | ||
} | ||
} | ||
|
||
handleDragOver(event) { | ||
event.preventDefault(); | ||
const container = event.target.closest(DRAGGABLE_CONTAINER); | ||
|
||
if (container) { | ||
container.classList.add("active_container"); | ||
} | ||
} | ||
|
||
handleDrop(event) { | ||
event.preventDefault(); | ||
const container = event.target.closest(DRAGGABLE_CONTAINER); | ||
if (!container || !this.draggedItem) return; | ||
|
||
container.classList.remove("active_container"); | ||
this.draggedItem.style.opacity = '1'; | ||
|
||
// Updated order of items as an array of item IDs | ||
const reorderedItems = Array.from(container.children) | ||
.filter(item => item.classList.contains("pb_draggable_item")) | ||
.map(item => item.id.replace("item_", "")); | ||
|
||
// Store reordered items in a data attribute on the container | ||
container.setAttribute("data-reordered-items", JSON.stringify(reorderedItems)); | ||
|
||
const customEvent = new CustomEvent('pb-draggable-reorder', { | ||
detail: { | ||
reorderedItems, | ||
containerId: container.id, | ||
} | ||
}); | ||
this.element.dispatchEvent(customEvent); | ||
|
||
this.draggedItem = null; | ||
this.draggedItemId = null; | ||
} | ||
|
||
|
||
handleDragEnd(event) { | ||
event.target.classList.remove("is_dragging"); | ||
event.target.style.opacity = '1'; | ||
this.draggedItem = null; | ||
this.draggedItemId = null; | ||
|
||
this.element.querySelectorAll(DRAGGABLE_CONTAINER).forEach(container => { | ||
container.classList.remove("active_container"); | ||
}); | ||
} | ||
} |