Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for pointerEvents (from HollowMan6) #143

Merged
merged 10 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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