Handle draggable lines on touch screens
This commit is contained in:
parent
048c003ff0
commit
d79deef5c0
@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
// SPDX-FileCopyrightText: 2025 Nicolas Chesnais <https://autre.space>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ import './styles/draggables.scss'
|
|||||||
*/
|
*/
|
||||||
export class DraggablesCustomElement extends CustomElement {
|
export class DraggablesCustomElement extends CustomElement {
|
||||||
currentDragged = null
|
currentDragged = null
|
||||||
|
droppableEl = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tag name for draggable elements.
|
* The tag name for draggable elements.
|
||||||
@ -37,6 +39,9 @@ export class DraggablesCustomElement extends CustomElement {
|
|||||||
this._handleDragLeaveBinded = this._handleDragLeave.bind(this)
|
this._handleDragLeaveBinded = this._handleDragLeave.bind(this)
|
||||||
this._handleDragEndBinded = this._handleDragEnd.bind(this)
|
this._handleDragEndBinded = this._handleDragEnd.bind(this)
|
||||||
this._handleDropBinded = this._handleDrop.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()
|
return super.initialize()
|
||||||
}
|
}
|
||||||
@ -44,21 +49,29 @@ export class DraggablesCustomElement extends CustomElement {
|
|||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
super.connectedCallback()
|
super.connectedCallback()
|
||||||
this.currentDragged = null
|
this.currentDragged = null
|
||||||
|
this.droppableEl = null
|
||||||
this.addEventListener('dragstart', this._handleDragStartBinded)
|
this.addEventListener('dragstart', this._handleDragStartBinded)
|
||||||
this.addEventListener('dragover', this._handleDragOverBinded)
|
this.addEventListener('dragover', this._handleDragOverBinded)
|
||||||
this.addEventListener('dragleave', this._handleDragLeaveBinded)
|
this.addEventListener('dragleave', this._handleDragLeaveBinded)
|
||||||
this.addEventListener('dragend', this._handleDragEndBinded)
|
this.addEventListener('dragend', this._handleDragEndBinded)
|
||||||
this.addEventListener('drop', this._handleDropBinded)
|
this.addEventListener('drop', this._handleDropBinded)
|
||||||
|
this.addEventListener('touchstart', this._handleTouchStartBinded)
|
||||||
|
this.addEventListener('touchmove', this._handleTouchMoveBinded)
|
||||||
|
this.addEventListener('touchend', this._handleTouchEndBinded)
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback () {
|
disconnectedCallback () {
|
||||||
super.disconnectedCallback()
|
super.disconnectedCallback()
|
||||||
this.currentDragged = null
|
this.currentDragged = null
|
||||||
|
this.droppableEl = null
|
||||||
this.removeEventListener('dragstart', this._handleDragStartBinded)
|
this.removeEventListener('dragstart', this._handleDragStartBinded)
|
||||||
this.removeEventListener('dragover', this._handleDragOverBinded)
|
this.removeEventListener('dragover', this._handleDragOverBinded)
|
||||||
this.removeEventListener('dragleave', this._handleDragLeaveBinded)
|
this.removeEventListener('dragleave', this._handleDragLeaveBinded)
|
||||||
this.removeEventListener('dragend', this._handleDragEndBinded)
|
this.removeEventListener('dragend', this._handleDragEndBinded)
|
||||||
this.removeEventListener('drop', this._handleDropBinded)
|
this.removeEventListener('drop', this._handleDropBinded)
|
||||||
|
this.removeEventListener('touchstart', this._handleTouchStartBinded)
|
||||||
|
this.removeEventListener('touchmove', this._handleTouchMoveBinded)
|
||||||
|
this.removeEventListener('touchend', this._handleTouchEndBinded)
|
||||||
}
|
}
|
||||||
|
|
||||||
_isADraggableEl (target) {
|
_isADraggableEl (target) {
|
||||||
@ -69,8 +82,7 @@ export class DraggablesCustomElement extends CustomElement {
|
|||||||
return target.closest?.(this.droppableTagNames.join(','))
|
return target.closest?.(this.droppableTagNames.join(','))
|
||||||
}
|
}
|
||||||
|
|
||||||
_isOnTopHalf (ev, el) {
|
_isOnTopHalf (y, el) {
|
||||||
const y = ev.clientY
|
|
||||||
const bounding = el.getBoundingClientRect()
|
const bounding = el.getBoundingClientRect()
|
||||||
return (y <= bounding.y + (bounding.height / 2))
|
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) {
|
_handleDragStart (ev) {
|
||||||
// The draggable=true is on a child bode
|
// The draggable=true is on a child bode
|
||||||
const possibleEl = ev.target.parentElement
|
const possibleEl = ev.target.parentElement
|
||||||
if (!this._isADraggableEl(possibleEl)) { return }
|
if (!this._isADraggableEl(possibleEl)) { return }
|
||||||
console.log('[livechat drag&drop] Starting to drag a ' + this.draggableTagName + '...')
|
this._setCurrentDragged(possibleEl)
|
||||||
this.currentDragged = possibleEl
|
|
||||||
this._resetDropOver()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleDragOver (ev) {
|
_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
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event says we should preventDefault
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
this._setDroppableClasses(droppableEl, ev.clientY)
|
||||||
// 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')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleDragLeave (ev) {
|
_handleDragLeave (ev) {
|
||||||
@ -124,16 +155,33 @@ export class DraggablesCustomElement extends CustomElement {
|
|||||||
|
|
||||||
let droppedOnEl = document.querySelector('.livechat-drag-bottom-half, .livechat-drag-top-half')
|
let droppedOnEl = document.querySelector('.livechat-drag-bottom-half, .livechat-drag-top-half')
|
||||||
droppedOnEl = this._getParentDroppableEl(droppedOnEl)
|
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 {
|
_handleTouchMove (ev) {
|
||||||
this._dropDone(this.currentDragged, droppedOnEl, droppedOnEl.classList.contains('livechat-drag-top-half'))
|
if (!this.currentDragged) return
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
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._resetDropOver()
|
||||||
|
this._setDroppableClasses(droppableEl, clientY)
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleTouchEnd (_ev) {
|
||||||
|
if (!this.currentDragged || !this.droppableEl) return
|
||||||
|
|
||||||
|
this._tryToDrop(this.droppableEl)
|
||||||
|
this.currentDragged = null
|
||||||
|
this.droppableEl = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
* SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nicolas Chesnais <https://autre.space>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
@ -16,4 +17,8 @@
|
|||||||
.livechat-drag-top-half > .draggables-line {
|
.livechat-drag-top-half > .draggables-line {
|
||||||
border-top: 4px solid blue;
|
border-top: 4px solid blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[draggable="true"] {
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user