Added drag & drop for anime list status tabs (closes #56)
This commit is contained in:
parent
9e3ec7656d
commit
a18aa0095a
@ -8,7 +8,7 @@ component AnimeList(animeListItems []*arn.AnimeListItem, nextIndex int, viewUser
|
|||||||
|
|
||||||
component AnimeListScrollable(animeListItems []*arn.AnimeListItem, viewUser *arn.User, user *arn.User)
|
component AnimeListScrollable(animeListItems []*arn.AnimeListItem, viewUser *arn.User, user *arn.User)
|
||||||
each item in animeListItems
|
each item in animeListItems
|
||||||
.anime-list-item.mountable(title=item.Notes, data-api="/api/animelist/" + viewUser.ID + "/field/Items[AnimeID=\"" + item.AnimeID + "\"]")
|
.anime-list-item.mountable(title=item.Notes, data-api="/api/animelist/" + viewUser.ID + "/field/Items[AnimeID=\"" + item.AnimeID + "\"]", draggable="true")
|
||||||
.anime-list-item-image-container
|
.anime-list-item-image-container
|
||||||
a.anime-list-item-image-link(href=item.Anime().Link())
|
a.anime-list-item-image-link(href=item.Anime().Link())
|
||||||
img.anime-list-item-image.lazy(data-src=item.Anime().ImageLink("small"), data-webp="true", data-color=item.Anime().AverageColor(), alt=item.Anime().Title.ByUser(user))
|
img.anime-list-item-image.lazy(data-src=item.Anime().ImageLink("small"), data-webp="true", data-color=item.Anime().AverageColor(), alt=item.Anime().Title.ByUser(user))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
component Tab(label string, icon string, url string)
|
component Tab(label string, icon string, url string)
|
||||||
a.tab.action(href=url, data-action="diff", data-trigger="click", aria-label=label)
|
a.tab.action(href=url, data-action="diff", data-trigger="click", aria-label=label, dropzone="move")
|
||||||
Icon(icon)
|
Icon(icon)
|
||||||
span.tab-text= label
|
span.tab-text= label
|
@ -330,88 +330,190 @@ export default class AnimeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dragAndDrop() {
|
dragAndDrop() {
|
||||||
for(let element of findAll("inventory-slot")) {
|
if(location.pathname.includes("/animelist/")) {
|
||||||
// Skip elements that have their event listeners attached already
|
for(let element of findAll("anime-list-item")) {
|
||||||
if(element["listeners-attached"]) {
|
// Skip elements that have their event listeners attached already
|
||||||
continue
|
if(element["drag-listeners-attached"]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
element.addEventListener("dragstart", e => {
|
||||||
|
if(!element.draggable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = element.getElementsByClassName("anime-list-item-image")[0]
|
||||||
|
e.dataTransfer.setDragImage(image, 0, 0)
|
||||||
|
|
||||||
|
let name = element.getElementsByClassName("anime-list-item-name")[0]
|
||||||
|
|
||||||
|
e.dataTransfer.setData("text/plain", JSON.stringify({
|
||||||
|
api: element.dataset.api,
|
||||||
|
animeTitle: name.textContent
|
||||||
|
}))
|
||||||
|
e.dataTransfer.effectAllowed = "move"
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// Prevent re-attaching the same listeners
|
||||||
|
element["drag-listeners-attached"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
element.addEventListener("dragstart", e => {
|
for(let element of findAll("tab")) {
|
||||||
if(!element.draggable) {
|
// Skip elements that have their event listeners attached already
|
||||||
return
|
if(element["drop-listeners-attached"]) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
e.dataTransfer.setData("text", element.dataset.index)
|
element.addEventListener("drop", async e => {
|
||||||
}, false)
|
let toElement = e.toElement as HTMLElement
|
||||||
|
|
||||||
element.addEventListener("dblclick", e => {
|
// Find tab element
|
||||||
if(!element.draggable) {
|
while(toElement && !toElement.classList.contains("tab")) {
|
||||||
return
|
toElement = toElement.parentElement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore a drop on the current status tab
|
||||||
|
if(!toElement || toElement.classList.contains("active")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = e.dataTransfer.getData("text/plain")
|
||||||
|
let json = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
json = JSON.parse(data)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!json || !json.api) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
let tabText = toElement.textContent
|
||||||
|
let newStatus = tabText.toLowerCase()
|
||||||
|
|
||||||
|
if(newStatus === "on hold") {
|
||||||
|
newStatus = "hold"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.post(json.api, {
|
||||||
|
Status: newStatus
|
||||||
|
})
|
||||||
|
await this.reloadContent()
|
||||||
|
|
||||||
|
this.statusMessage.showInfo(`Moved "${json.animeTitle}" to "${tabText}".`)
|
||||||
|
} catch(err) {
|
||||||
|
this.statusMessage.showError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
element.addEventListener("dragenter", e => {
|
||||||
|
e.preventDefault()
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
element.addEventListener("dragleave", e => {
|
||||||
|
e.preventDefault()
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
element.addEventListener("dragover", e => {
|
||||||
|
e.preventDefault()
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// Prevent re-attaching the same listeners
|
||||||
|
element["drop-listeners-attached"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(location.pathname.startsWith("/inventory")) {
|
||||||
|
for(let element of findAll("inventory-slot")) {
|
||||||
|
// Skip elements that have their event listeners attached already
|
||||||
|
if(element["drag-listeners-attached"]) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemName = element.getAttribute("aria-label")
|
element.addEventListener("dragstart", e => {
|
||||||
|
if(!element.draggable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if(element.dataset.consumable !== "true") {
|
e.dataTransfer.setData("text", element.dataset.index)
|
||||||
return this.statusMessage.showError(itemName + " is not a consumable item.")
|
}, false)
|
||||||
}
|
|
||||||
|
|
||||||
let apiEndpoint = this.findAPIEndpoint(element)
|
element.addEventListener("dblclick", e => {
|
||||||
|
if(!element.draggable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.post(apiEndpoint + "/use/" + element.dataset.index, "")
|
let itemName = element.getAttribute("aria-label")
|
||||||
.then(() => this.reloadContent())
|
|
||||||
.then(() => this.statusMessage.showInfo(`You used ${itemName}.`))
|
|
||||||
.catch(err => this.statusMessage.showError(err))
|
|
||||||
}, false)
|
|
||||||
|
|
||||||
element.addEventListener("dragenter", e => {
|
if(element.dataset.consumable !== "true") {
|
||||||
element.classList.add("drag-enter")
|
return this.statusMessage.showError(itemName + " is not a consumable item.")
|
||||||
}, false)
|
}
|
||||||
|
|
||||||
element.addEventListener("dragleave", e => {
|
let apiEndpoint = this.findAPIEndpoint(element)
|
||||||
element.classList.remove("drag-enter")
|
|
||||||
}, false)
|
|
||||||
|
|
||||||
element.addEventListener("dragover", e => {
|
this.post(apiEndpoint + "/use/" + element.dataset.index, "")
|
||||||
e.preventDefault()
|
.then(() => this.reloadContent())
|
||||||
}, false)
|
.then(() => this.statusMessage.showInfo(`You used ${itemName}.`))
|
||||||
|
.catch(err => this.statusMessage.showError(err))
|
||||||
|
}, false)
|
||||||
|
|
||||||
element.addEventListener("drop", e => {
|
element.addEventListener("dragenter", e => {
|
||||||
let toElement = e.toElement as HTMLElement
|
element.classList.add("drag-enter")
|
||||||
toElement.classList.remove("drag-enter")
|
}, false)
|
||||||
|
|
||||||
e.stopPropagation()
|
element.addEventListener("dragleave", e => {
|
||||||
e.preventDefault()
|
element.classList.remove("drag-enter")
|
||||||
|
}, false)
|
||||||
|
|
||||||
let inventory = e.toElement.parentElement
|
element.addEventListener("dragover", e => {
|
||||||
let fromIndex = e.dataTransfer.getData("text")
|
e.preventDefault()
|
||||||
|
}, false)
|
||||||
|
|
||||||
if(!fromIndex) {
|
element.addEventListener("drop", e => {
|
||||||
return
|
let toElement = e.toElement as HTMLElement
|
||||||
}
|
toElement.classList.remove("drag-enter")
|
||||||
|
|
||||||
let fromElement = inventory.childNodes[fromIndex] as HTMLElement
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
let toIndex = toElement.dataset.index
|
let inventory = e.toElement.parentElement
|
||||||
|
let fromIndex = e.dataTransfer.getData("text")
|
||||||
|
|
||||||
if(fromElement === toElement || fromIndex === toIndex) {
|
if(!fromIndex) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap in database
|
let fromElement = inventory.childNodes[fromIndex] as HTMLElement
|
||||||
let apiEndpoint = this.findAPIEndpoint(inventory)
|
|
||||||
|
|
||||||
this.post(apiEndpoint + "/swap/" + fromIndex + "/" + toIndex, "")
|
let toIndex = toElement.dataset.index
|
||||||
.catch(err => this.statusMessage.showError(err))
|
|
||||||
|
|
||||||
// Swap in UI
|
if(fromElement === toElement || fromIndex === toIndex) {
|
||||||
swapElements(fromElement, toElement)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fromElement.dataset.index = toIndex
|
// Swap in database
|
||||||
toElement.dataset.index = fromIndex
|
let apiEndpoint = this.findAPIEndpoint(inventory)
|
||||||
}, false)
|
|
||||||
|
|
||||||
// Prevent re-attaching the same listeners
|
this.post(apiEndpoint + "/swap/" + fromIndex + "/" + toIndex, "")
|
||||||
element["listeners-attached"] = true
|
.catch(err => this.statusMessage.showError(err))
|
||||||
|
|
||||||
|
// Swap in UI
|
||||||
|
swapElements(fromElement, toElement)
|
||||||
|
|
||||||
|
fromElement.dataset.index = toIndex
|
||||||
|
toElement.dataset.index = fromIndex
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// Prevent re-attaching the same listeners
|
||||||
|
element["drag-listeners-attached"] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user