diff --git a/conversejs/custom/shared/components/draggables/index.js b/conversejs/custom/shared/components/draggables/index.js
index 2147a367..068cb50e 100644
--- a/conversejs/custom/shared/components/draggables/index.js
+++ b/conversejs/custom/shared/components/draggables/index.js
@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2024 John Livingston
+// SPDX-FileCopyrightText: 2025 Nicolas Chesnais
//
// SPDX-License-Identifier: AGPL-3.0-only
@@ -11,6 +12,7 @@ import './styles/draggables.scss'
*/
export class DraggablesCustomElement extends CustomElement {
currentDragged = null
+ droppableEl = null
/**
* The tag name for draggable elements.
@@ -37,6 +39,9 @@ export class DraggablesCustomElement extends CustomElement {
this._handleDragLeaveBinded = this._handleDragLeave.bind(this)
this._handleDragEndBinded = this._handleDragEnd.bind(this)
this._handleDropBinded = this._handleDrop.bind(this)
+ this._handleTouchStartBinded = this._handleTouchStart.bind(this)
+ this._handleTouchMoveBinded = this._handleTouchMove.bind(this)
+ this._handleTouchEndBinded = this._handleTouchEnd.bind(this)
return super.initialize()
}
@@ -44,21 +49,29 @@ export class DraggablesCustomElement extends CustomElement {
connectedCallback () {
super.connectedCallback()
this.currentDragged = null
+ this.droppableEl = null
this.addEventListener('dragstart', this._handleDragStartBinded)
this.addEventListener('dragover', this._handleDragOverBinded)
this.addEventListener('dragleave', this._handleDragLeaveBinded)
this.addEventListener('dragend', this._handleDragEndBinded)
this.addEventListener('drop', this._handleDropBinded)
+ this.addEventListener('touchstart', this._handleTouchStartBinded)
+ this.addEventListener('touchmove', this._handleTouchMoveBinded)
+ this.addEventListener('touchend', this._handleTouchEndBinded)
}
disconnectedCallback () {
super.disconnectedCallback()
this.currentDragged = null
+ this.droppableEl = null
this.removeEventListener('dragstart', this._handleDragStartBinded)
this.removeEventListener('dragover', this._handleDragOverBinded)
this.removeEventListener('dragleave', this._handleDragLeaveBinded)
this.removeEventListener('dragend', this._handleDragEndBinded)
this.removeEventListener('drop', this._handleDropBinded)
+ this.removeEventListener('touchstart', this._handleTouchStartBinded)
+ this.removeEventListener('touchmove', this._handleTouchMoveBinded)
+ this.removeEventListener('touchend', this._handleTouchEndBinded)
}
_isADraggableEl (target) {
@@ -69,8 +82,7 @@ export class DraggablesCustomElement extends CustomElement {
return target.closest?.(this.droppableTagNames.join(','))
}
- _isOnTopHalf (ev, el) {
- const y = ev.clientY
+ _isOnTopHalf (y, el) {
const bounding = el.getBoundingClientRect()
return (y <= bounding.y + (bounding.height / 2))
}
@@ -81,13 +93,39 @@ export class DraggablesCustomElement extends CustomElement {
)
}
+ _setCurrentDragged (draggedEl) {
+ console.log('[livechat drag&drop] Starting to drag a ' + this.draggableTagName + '...')
+ this.currentDragged = draggedEl
+ this._resetDropOver()
+ }
+
+ _setDroppableClasses (droppableEl, y) {
+ // Are we on the top or bottom part of the droppableEl?
+ let topHalf = false
+ if (!this.droppableAlwaysBottomTagNames.includes(droppableEl.nodeName.toLowerCase())) {
+ topHalf = this._isOnTopHalf(y, droppableEl)
+ }
+ droppableEl.classList.add(topHalf ? 'livechat-drag-top-half' : 'livechat-drag-bottom-half')
+ droppableEl.classList.remove(topHalf ? 'livechat-drag-bottom-half' : 'livechat-drag-top-half')
+ }
+
+ _tryToDrop (droppedOnEl) {
+ if (!droppedOnEl) return
+
+ console.log('[livechat drag&drop] ' + this.draggableTagName + ' dropped...')
+ try {
+ this._dropDone(this.currentDragged, droppedOnEl, droppedOnEl.classList.contains('livechat-drag-top-half'))
+ } catch (err) {
+ console.error(err)
+ }
+ this._resetDropOver()
+ }
+
_handleDragStart (ev) {
// The draggable=true is on a child bode
const possibleEl = ev.target.parentElement
if (!this._isADraggableEl(possibleEl)) { return }
- console.log('[livechat drag&drop] Starting to drag a ' + this.draggableTagName + '...')
- this.currentDragged = possibleEl
- this._resetDropOver()
+ this._setCurrentDragged(possibleEl)
}
_handleDragOver (ev) {
@@ -97,14 +135,7 @@ export class DraggablesCustomElement extends CustomElement {
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event says we should preventDefault
ev.preventDefault()
-
- // Are we on the top or bottom part of the droppableEl?
- let topHalf = false
- if (!this.droppableAlwaysBottomTagNames.includes(droppableEl.nodeName.toLowerCase())) {
- topHalf = this._isOnTopHalf(ev, droppableEl)
- }
- droppableEl.classList.add(topHalf ? 'livechat-drag-top-half' : 'livechat-drag-bottom-half')
- droppableEl.classList.remove(topHalf ? 'livechat-drag-bottom-half' : 'livechat-drag-top-half')
+ this._setDroppableClasses(droppableEl, ev.clientY)
}
_handleDragLeave (ev) {
@@ -124,16 +155,33 @@ export class DraggablesCustomElement extends CustomElement {
let droppedOnEl = document.querySelector('.livechat-drag-bottom-half, .livechat-drag-top-half')
droppedOnEl = this._getParentDroppableEl(droppedOnEl)
- if (!droppedOnEl) { return }
+ this._tryToDrop(droppedOnEl)
+ }
- console.log('[livechat drag&drop] ' + this.draggableTagName + ' dropped...')
+ _handleTouchStart (ev) {
+ const possibleEl = this._getParentDroppableEl(ev.target)
+ if (!possibleEl) return
+ this._setCurrentDragged(possibleEl)
+ }
- try {
- this._dropDone(this.currentDragged, droppedOnEl, droppedOnEl.classList.contains('livechat-drag-top-half'))
- } catch (err) {
- console.error(err)
- }
+ _handleTouchMove (ev) {
+ if (!this.currentDragged) return
+
+ const { clientX, clientY } = ev.touches[0]
+ const droppableEl = this._getParentDroppableEl(document.elementFromPoint(clientX, clientY))
+ if (!droppableEl || droppableEl === this.droppableEl) return
+
+ this.droppableEl = droppableEl
this._resetDropOver()
+ this._setDroppableClasses(droppableEl, clientY)
+ }
+
+ _handleTouchEnd (_ev) {
+ if (!this.currentDragged || !this.droppableEl) return
+
+ this._tryToDrop(this.droppableEl)
+ this.currentDragged = null
+ this.droppableEl = null
}
/**
diff --git a/conversejs/custom/shared/components/draggables/styles/draggables.scss b/conversejs/custom/shared/components/draggables/styles/draggables.scss
index 93279389..240dec95 100644
--- a/conversejs/custom/shared/components/draggables/styles/draggables.scss
+++ b/conversejs/custom/shared/components/draggables/styles/draggables.scss
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2024 John Livingston
+ * SPDX-FileCopyrightText: 2025 Nicolas Chesnais
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -16,4 +17,8 @@
.livechat-drag-top-half > .draggables-line {
border-top: 4px solid blue;
}
+
+ [draggable="true"] {
+ touch-action: none;
+ }
}