Skip to content

Commit

Permalink
Merge pull request #143 from ThibaultJanBeyer/HollowMan6-master
Browse files Browse the repository at this point in the history
Add support for pointerEvents (from HollowMan6)
  • Loading branch information
ThibaultJanBeyer committed Nov 4, 2022
2 parents 12487d9 + cbff368 commit aebf913
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
# next

- Add support for `pointerEvents` (from @HollowMan6) [#143](https://github.com/ThibaultJanBeyer/DragSelect/pull/143) & [#128](https://github.com/ThibaultJanBeyer/DragSelect/pull/128)

# 2.4.3

- add `triggerCallback` option to `removeSelectables` and `addSelectables`
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -301,6 +301,7 @@ Here is the full list:
|hoverClass |string |The class name assigned to the mouse hovered items. |[see classes](#classes)
|selectorClass |string |The class name assigned to the square selector helper. |[see classes](#classes)
|selectableClass |string |The class name assigned to the elements that can be selected. |[see classes](#classes)
|usePointerEvents |boolean |Whether to use Pointer Events to replace traditional Mouse or Touch Events. Useful for tools like Google Blockly. |`false`
## Post-Initialization Setting-Updates
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/methods/getPointerPos.js
Expand Up @@ -4,7 +4,7 @@ import '../types'
/**
* Returns cursor x, y position based on event object
* @param {Object} p
* @param {MouseEvent|Touch} p.event
* @param {MouseEvent|Touch|PointerEvent} p.event
* @return {Vect2} cursor X/Y position
*/
export default ({ event }) => ({
Expand Down
1 change: 1 addition & 0 deletions src/methods/hydrateSettings.js
Expand Up @@ -101,6 +101,7 @@ export default (settings, withFallback) => ({
...hydrateHelper('dragKeys', settings.dragKeys, withFallback, { up: ['ArrowUp'], down: ['ArrowDown'], left: ['ArrowLeft'], right: ['ArrowRight'] }),
...hydrateHelper('keyboardDragSpeed', settings.keyboardDragSpeed, withFallback, 10),
...hydrateHelper('useTransform', settings.useTransform, withFallback, true),
...hydrateHelper('usePointerEvents', settings.usePointerEvents, withFallback, false),
...hydrateHelper('hoverClass', settings.hoverClass, withFallback, 'ds-hover'),
...hydrateHelper('selectableClass', settings.selectableClass, withFallback, 'ds-selectable'),
...hydrateHelper('selectedClass', settings.selectedClass, withFallback, 'ds-selected'),
Expand Down
39 changes: 32 additions & 7 deletions src/modules/Interaction.js
Expand Up @@ -15,6 +15,7 @@ export default class Interaction {
*/
constructor({ DS }) {
this.DS = DS
this.Settings = DS.stores.SettingsStore.s
// @ts-ignore: @todo: update to typescript
this.DS.subscribe('Settings:updated:area', this.init)
this.DS.subscribe('PointerStore:updated', this.update)
Expand All @@ -31,7 +32,14 @@ export default class Interaction {
init = () => this.DS.publish('Interaction:init:pre', {})
_init = () => {
this.stop()
this.DS.Area.HTMLNode.addEventListener('mousedown', this.start)

// @TODO: fix pointer events mixing issue see [PR](https://github.com/ThibaultJanBeyer/DragSelect/pull/128#issuecomment-1154885289)
if (this.Settings.usePointerEvents)
this.DS.Area.HTMLNode.addEventListener('pointerdown', this.start, {
passive: false,
})
else this.DS.Area.HTMLNode.addEventListener('mousedown', this.start)

this.DS.Area.HTMLNode.addEventListener('touchstart', this.start, {
passive: false,
})
Expand Down Expand Up @@ -79,7 +87,12 @@ export default class Interaction {

this.DS.publish('Interaction:start', { event, isDragging: this.isDragging })

document.addEventListener('mouseup', this.reset)
// @TODO: fix pointer events mixing issue see [PR](https://github.com/ThibaultJanBeyer/DragSelect/pull/128#issuecomment-1154885289)
if (this.Settings.usePointerEvents) {
document.addEventListener('pointerup', this.reset)
document.addEventListener('pointercancel', this.reset)
} else document.addEventListener('mouseup', this.reset)

document.addEventListener('touchend', this.reset)
}

Expand All @@ -90,17 +103,17 @@ export default class Interaction {
*/
isDragEvent = (event) => {
const clickedElement = /** @type {Element} */ (event.target).closest(
`.${this.DS.stores.SettingsStore.s.selectableClass}`
`.${this.Settings.selectableClass}`
)

if (
!this.DS.stores.SettingsStore.s.draggability ||
!this.Settings.draggability ||
this.DS.stores.KeyStore.isMultiSelectKeyPressed(event) ||
!clickedElement
)
return false

if (this.DS.stores.SettingsStore.s.immediateDrag) {
if (this.Settings.immediateDrag) {
if (!this.DS.SelectedSet.size)
this.DS.SelectedSet.add(/** @type {DSElement} */ (clickedElement))
else if (!this.DS.SelectedSet.has(clickedElement)) {
Expand Down Expand Up @@ -144,12 +157,24 @@ export default class Interaction {
stop = () => {
this.isInteracting = false
this.isDragging = false
this.DS.Area.HTMLNode.removeEventListener('mousedown', this.start)

// @TODO: fix pointer events mixing issue see [PR](https://github.com/ThibaultJanBeyer/DragSelect/pull/128#issuecomment-1154885289)
if (this.Settings.usePointerEvents) {
this.DS.Area.HTMLNode.removeEventListener('pointerdown', this.start, {
// @ts-ignore
passive: false,
})
document.removeEventListener('pointerup', this.reset)
document.removeEventListener('pointercancel', this.reset)
} else {
this.DS.Area.HTMLNode.removeEventListener('mousedown', this.start)
document.removeEventListener('mouseup', this.reset)
}

this.DS.Area.HTMLNode.removeEventListener('touchstart', this.start, {
// @ts-ignore
passive: false,
})
document.removeEventListener('mouseup', this.reset)
document.removeEventListener('touchend', this.reset)
}

Expand Down
31 changes: 23 additions & 8 deletions src/modules/SelectableSet.js
Expand Up @@ -13,6 +13,7 @@ export default class SelectableSet extends Set {
constructor({ DS }) {
super()
this.DS = DS
this.Settings = DS.stores.SettingsStore.s
this.DS.subscribe('Interaction:init', this.init)
// @ts-ignore: @todo: update to typescript
this.DS.PubSub.subscribe('Settings:updated:selectables', () => {
Expand All @@ -29,7 +30,7 @@ export default class SelectableSet extends Set {
}

init = () =>
toArray(this.DS.stores.SettingsStore.s.selectables).forEach((el) =>
toArray(this.Settings.selectables).forEach((el) =>
this.add(el)
)

Expand All @@ -41,17 +42,24 @@ export default class SelectableSet extends Set {
item: element,
}
this.DS.publish('Selectable:added:pre', publishData)
element.classList.add(this.DS.stores.SettingsStore.s.selectableClass)
element.classList.add(this.Settings.selectableClass)
element.addEventListener('click', this._onClick)
element.addEventListener('mousedown', this._onPointer)

if (this.Settings.usePointerEvents)
element.addEventListener('pointerdown', this._onPointer, {
// @ts-ignore
passive: false,
})
else element.addEventListener('mousedown', this._onPointer)

element.addEventListener('touchstart', this._onPointer, {
// @ts-ignore
passive: false,
})

if (
this.DS.stores.SettingsStore.s.draggability &&
!this.DS.stores.SettingsStore.s.useTransform
this.Settings.draggability &&
!this.Settings.useTransform
)
handleElementPositionAttribute({
computedStyle: window.getComputedStyle(element),
Expand All @@ -70,10 +78,17 @@ export default class SelectableSet extends Set {
item: element,
}
this.DS.publish('Selectable:removed:pre', publishData)
element.classList.remove(this.DS.stores.SettingsStore.s.selectableClass)
element.classList.remove(this.DS.stores.SettingsStore.s.hoverClass)
element.classList.remove(this.Settings.selectableClass)
element.classList.remove(this.Settings.hoverClass)
element.removeEventListener('click', this._onClick)
element.removeEventListener('mousedown', this._onPointer)

if (this.Settings.usePointerEvents)
element.removeEventListener('pointerdown', this._onPointer, {
// @ts-ignore
passive: false,
})
else element.removeEventListener('mousedown', this._onPointer)

element.removeEventListener('touchstart', this._onPointer, {
// @ts-ignore
passive: false,
Expand Down
2 changes: 1 addition & 1 deletion src/stores/KeyStore.js
Expand Up @@ -60,7 +60,7 @@ export default class KeyStore {

reset = () => this._currentValues.clear()

/** @param {KeyboardEvent|MouseEvent|TouchEvent} [event] */
/** @param {KeyboardEvent|MouseEvent|PointerEvent|TouchEvent} [event] */
isMultiSelectKeyPressed(event) {
if(this.DS.stores.SettingsStore.s.multiSelectMode) return true
const multiSelectKeys = this.DS.stores.SettingsStore.s.multiSelectKeys.map(
Expand Down
20 changes: 17 additions & 3 deletions src/stores/PointerStore.js
Expand Up @@ -53,13 +53,20 @@ export default class PointerStore {
*/
constructor({ DS }) {
this.DS = DS
this.Settings = DS.stores.SettingsStore.s
this.DS.subscribe('Interaction:init', this.init)
this.DS.subscribe('Interaction:start', ({ event }) => this.start(event))
this.DS.subscribe('Interaction:end', ({ event }) => this.reset(event))
}

init = () => {
document.addEventListener('mousemove', this.update)
if (this.Settings.usePointerEvents)
document.addEventListener('pointermove', this.update, {
// @ts-ignore
passive: false,
})
else document.addEventListener('mousemove', this.update)

document.addEventListener('touchmove', this.update, {
// @ts-ignore
passive: false,
Expand Down Expand Up @@ -89,7 +96,14 @@ export default class PointerStore {
}

stop = () => {
document.removeEventListener('mousemove', this.update)
// @TODO: fix pointer events mixing issue see [PR](https://github.com/ThibaultJanBeyer/DragSelect/pull/128#issuecomment-1154885289)
if (this.Settings.usePointerEvents)
document.removeEventListener('pointermove', this.update, {
// @ts-ignore
passive: false,
})
else document.removeEventListener('mousemove', this.update)

document.removeEventListener('touchmove', this.update, {
// @ts-ignore
passive: false,
Expand All @@ -110,7 +124,7 @@ export default class PointerStore {

/**
* @param {DSEvent} event
* @return {MouseEvent|Touch}
* @return {MouseEvent|PointerEvent|Touch}
* @private
*/
_normalizedEvent(event) {
Expand Down
5 changes: 3 additions & 2 deletions src/types.js
Expand Up @@ -17,6 +17,7 @@
* @property {DSDragKeys} [dragKeys={up:['ArrowUp'],down:['ArrowDown'],left:['ArrowLeft'],righ:['ArrowRight']}] The keys available to drag element using the keyboard.
* @property {number} [keyboardDragSpeed=10] The speed at which elements are dragged using the keyboard. In pixels per keydown.
* @property {boolean} [useTransform=true] Whether to use hardware accelerated css transforms when dragging or top/left instead
* @property {boolean} [usePointerEvents=false] Whether to use Pointer Events to replace traditional Mouse or Touch Events. Useful for tools like Google Blockly.
* @property {string} [hoverClass=ds-hover] the class assigned to the mouse hovered items
* @property {string} [selectableClass=ds-selectable] the class assigned to the elements that can be selected
* @property {string} [selectedClass=ds-selected] the class assigned to the selected items
Expand All @@ -28,7 +29,7 @@
* The Object that is passed back to any callback method
* @typedef {Object} CallbackObject
* @property {Array<HTMLElement|SVGElement|any>} [items] The items currently selected
* @property {MouseEvent|TouchEvent|KeyboardEvent|Event} [event] The respective event object
* @property {MouseEvent|TouchEvent|PointerEvent|KeyboardEvent|Event} [event] The respective event object
* @property {HTMLElement|SVGElement|any} [item] The single item currently interacted with
* @property {boolean} [isDragging] Whether the interaction is a drag or a select
* @property {boolean} [isDraggingKeyboard] Whether or not the drag interaction is via keyboard
Expand All @@ -51,7 +52,7 @@
/** @typedef {Array.<HTMLElement|SVGElement> | HTMLElement | SVGElement} DSInputElements the elements that can be selected */
/** @typedef {Array.<HTMLElement|SVGElement>} DSElements the elements that can be selected */
/** @typedef {HTMLElement|SVGElement} DSElement a single element that can be selected */
/** @typedef {MouseEvent|TouchEvent} DSEvent en event from a touch or mouse interaction */
/** @typedef {MouseEvent|TouchEvent|PointerEvent} DSEvent en event from a touch or mouse interaction */
/** @typedef {Array.<'Shift'|'Control'|'Meta'|string>} DSMultiSelectKeys An array of keys that allows switching to the multi-select mode */

/** @typedef {'dragmove'|'autoscroll'|'dragstart'|'elementselect'|'elementunselect'|'callback'} DSEventNames */
Expand Down

0 comments on commit aebf913

Please sign in to comment.