-
Notifications
You must be signed in to change notification settings - Fork 7
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
[DO NOT MERGE] feat: allow single pointer drag #53
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -15,6 +15,7 @@ const touchRegExp = /touch/; | |||||
// 300ms is the usual mouse interval; | ||||||
// // However, an underpowered mobile device under a heavy load may queue mouse events for a longer period. | ||||||
const IGNORE_MOUSE_TIMEOUT = 2000; | ||||||
const ESC = 27; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We better extract this to an enum-like entity like Other keys can be added to this enum and it can be reused. |
||||||
|
||||||
function normalizeEvent(e) { | ||||||
if (e.type.match(touchRegExp)) { | ||||||
|
@@ -51,16 +52,19 @@ export class Draggable { | |||||
|
||||||
get document() { | ||||||
return this._element | ||||||
? this._element.ownerDocument | ||||||
: document; | ||||||
? this._element.ownerDocument | ||||||
: document; | ||||||
} | ||||||
|
||||||
constructor({ press = noop, drag = noop, release = noop, mouseOnly = false }) { | ||||||
constructor({ press = noop, drag = noop, release = noop, cancel = noop, mouseOnly = false, clickMoveClick = false }) { | ||||||
this._pressHandler = proxy(normalizeEvent, press); | ||||||
this._dragHandler = proxy(normalizeEvent, drag); | ||||||
this._releaseHandler = proxy(normalizeEvent, release); | ||||||
this._cancelHandler = proxy(normalizeEvent, cancel); | ||||||
this._ignoreMouse = false; | ||||||
this._isDragging = false; | ||||||
this._mouseOnly = mouseOnly; | ||||||
this._clickMoveClick = clickMoveClick; | ||||||
|
||||||
this._touchstart = (e) => { | ||||||
if (e.touches.length === 1) { | ||||||
|
@@ -92,12 +96,31 @@ export class Draggable { | |||||
const { which } = e; | ||||||
|
||||||
if ((which && which > 1) || this._ignoreMouse) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that event.which is marked as deprecated, we can think about alternatives like https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons and https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button |
||||||
bind(this.document, "contextmenu", preventDefault); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We better add a comment on why this is necessary (e.g. add info that we need the right mouse button for canceling the drag action). It is also worth it to consider if there is a very very specific case if you stop dragging on something, but expect to open the context menu, e.g. if customers have added a ContextMenu component and expect it to be opened. |
||||||
|
||||||
this.cancelDrag(e); | ||||||
|
||||||
this._cancelHandler(e); | ||||||
Comment on lines
+101
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we better reverse the order and act depending on whether the The above is only true if we actually want to allow preventing events, which I don't think is currently supported in the |
||||||
|
||||||
return; | ||||||
} | ||||||
|
||||||
bind(this.document, "mousemove", this._mousemove); | ||||||
bind(this.document, "mouseup", this._mouseup); | ||||||
this._pressHandler(e); | ||||||
if (!this._clickMoveClick) { | ||||||
bind(this.document, "mouseup", this._mouseup); | ||||||
bind(this.document, "mousemove", this._mousemove); | ||||||
bind(this.document, "contextmenu", preventDefault); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This event wasn't prevented in the original version (and will prevent the "contextmenu", which might be interpreted as a regression) - it seems better not to have it here, but only when the |
||||||
this._pressHandler(e); | ||||||
} else { | ||||||
if (this._isDragging) { | ||||||
bind(this.document, "mouseup", this._mouseup); | ||||||
} else { | ||||||
bind(this.document, "mousemove", this._mousemove); | ||||||
Comment on lines
+114
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add a comment about why |
||||||
bind(this.document, "keydown", this._keydown); | ||||||
this._pressHandler(e); | ||||||
} | ||||||
Comment on lines
+108
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can reverse the condition:
By doing this we ask for a positive flag - whether dragging is enabled in a |
||||||
|
||||||
this._isDragging = !this._isDragging; | ||||||
} | ||||||
}; | ||||||
|
||||||
this._mousemove = (e) => { | ||||||
|
@@ -107,17 +130,41 @@ export class Draggable { | |||||
this._mouseup = (e) => { | ||||||
unbind(this.document, "mousemove", this._mousemove); | ||||||
unbind(this.document, "mouseup", this._mouseup); | ||||||
|
||||||
if (this.clickMoveClick){ | ||||||
unbind(this.document, "keydown", this._keydown); | ||||||
unbind(this.document, "contextmenu", preventDefault); | ||||||
} | ||||||
Comment on lines
+135
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to unbind other events, e.g.: |
||||||
|
||||||
this._releaseHandler(e); | ||||||
}; | ||||||
|
||||||
this._pointerdown = (e) => { | ||||||
if (e.isPrimary && e.button === 0) { | ||||||
bind(this.document, "pointermove", this._pointermove); | ||||||
bind(this.document, "pointerup", this._pointerup); | ||||||
bind(this.document, "pointercancel", this._pointerup); | ||||||
bind(this.document, "contextmenu", preventDefault); | ||||||
|
||||||
this._pressHandler(e); | ||||||
if (!this._clickMoveClick) { | ||||||
bind(this.document, "pointerup", this._pointerup); | ||||||
bind(this.document, "pointercancel", this._pointerup); | ||||||
bind(this.document, "contextmenu", preventDefault); | ||||||
bind(this.document, "pointermove", this._pointermove); | ||||||
this._pressHandler(e); | ||||||
} else { | ||||||
if (this._isDragging) { | ||||||
bind(this.document, "pointerup", this._pointerup); | ||||||
bind(this.document, "pointercancel", this._pointerup); | ||||||
} else { | ||||||
bind(this.document, "pointermove", this._pointermove); | ||||||
bind(this.document, "keydown", this._keydown); | ||||||
this._pressHandler(e); | ||||||
Comment on lines
+146
to
+158
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that it is easy for us to attach more than 1 event handler here for the same event as we have some logic for binding and unbinding events. As we do not want to have more than 1 handler - what about unbinding the event in the |
||||||
} | ||||||
|
||||||
this._isDragging = !this._isDragging; | ||||||
} | ||||||
} | ||||||
|
||||||
if (this._clickMoveClick && e.button == 2) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can extract an enum like |
||||||
this.cancelDrag(e); | ||||||
this._cancelHandler(e); | ||||||
} | ||||||
}; | ||||||
|
||||||
|
@@ -134,14 +181,25 @@ export class Draggable { | |||||
unbind(this.document, "pointercancel", this._pointerup); | ||||||
unbind(this.document, "contextmenu", preventDefault); | ||||||
|
||||||
if (this.clickMoveClick){ | ||||||
unbind(this.document, "keydown", this._keydown); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to unbind other events, e.g.: |
||||||
} | ||||||
|
||||||
this._releaseHandler(e); | ||||||
} | ||||||
}; | ||||||
|
||||||
this._keydown = (e) => { | ||||||
if (e.keyCode === ESC) { | ||||||
this.cancelDrag(e); | ||||||
this._cancelHandler(e); | ||||||
} | ||||||
Comment on lines
+193
to
+196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this behavior can be an option - We discussed this offline - some suites like Kendo Angular/React might want to have their own events or specific navigation and want to cancel the drag on Escape optionally. |
||||||
}; | ||||||
} | ||||||
|
||||||
bindTo(element) { | ||||||
if (element === this._element) { | ||||||
return; | ||||||
return; | ||||||
} | ||||||
|
||||||
if (this._element) { | ||||||
|
@@ -152,6 +210,18 @@ export class Draggable { | |||||
this._bindToCurrent(); | ||||||
} | ||||||
|
||||||
cancelDrag() { | ||||||
unbind(this.document, "pointermove", this._pointermove); | ||||||
unbind(this.document, "pointerup", this._pointerup); | ||||||
unbind(this.document, "pointercancel", this._pointerup); | ||||||
|
||||||
if (this.clickMoveClick) { | ||||||
unbind(this.document, "contextmenu", preventDefault); | ||||||
} | ||||||
|
||||||
this._isDragging = !this._isDragging; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
_bindToCurrent() { | ||||||
const element = this._element; | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggetions for naming changes are posted in the spec:
#52 (comment)