Skip to content

Commit

Permalink
Support for List<Element>
Browse files Browse the repository at this point in the history
Support for creating a Draggable with `List<Element>` (in addition to `ElementList` and `Element`).
  • Loading branch information
marcojakob committed Jun 21, 2018
1 parent d3e1919 commit b8578a0
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 119 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog

## Version 1.2.0 (2018-06-21)

- Support for creating a Draggable with `List<Element>` (in addition to `ElementList` and `Element`).

## Version 1.1.0 (2018-06-21)

- Remove cursor files. Support for `grab`, `-webkit-grab`, `grabbing`, and `-webkit-grabbing` is good enough.
Expand Down
1 change: 1 addition & 0 deletions example/basic/example.dart
@@ -1,4 +1,5 @@
import 'dart:html';

import 'package:dnd/dnd.dart';

/// A basic example of how to use [Draggable]s and [Dropzone]s together.
Expand Down
26 changes: 13 additions & 13 deletions lib/src/draggable.dart
Expand Up @@ -105,14 +105,15 @@ class Draggable {
// -------------------
// Private Properties
// -------------------
/// The [Element] or [ElementList] on which a drag is detected.
final _elementOrElementList;
/// The list of [Element]s on which a drag is detected.
List<Element> _elements;

/// Managers for browser events.
final List<_EventManager> _eventManagers = [];

/// Creates a new [Draggable] for [elementOrElementList]. The
/// [elementOrElementList] must be of type [Element] or [ElementList].
/// [elementOrElementList] must be of type [Element], [ElementList], or
/// [List<Element>].
///
///
/// ## Options
Expand Down Expand Up @@ -154,8 +155,12 @@ class Draggable {
this.handle: null,
this.cancel: 'input, textarea, button, select, option',
this.draggingClass: 'dnd-dragging',
this.draggingClassBody: 'dnd-drag-occurring'})
: this._elementOrElementList = elementOrElementList {
this.draggingClassBody: 'dnd-drag-occurring'}) {
// Wrap in a List if it is not a list but a single Element.
_elements = elementOrElementList is List
? elementOrElementList
: [elementOrElementList];

// Detect Pointer Event Support.
JsObject jsWindow = new JsObject.fromBrowserObject(window);

Expand Down Expand Up @@ -262,7 +267,7 @@ class Draggable {
_currentDrag.startPosition.distanceTo(_currentDrag.position) >
clickSuppression)) {
// Prevent MouseEvent from firing a click after mouseUp event if the move was significant.
_suppressClickEvent();
_suppressClickEvent(_currentDrag.element);
}

// Remove the css classes.
Expand All @@ -281,9 +286,8 @@ class Draggable {
/// Makes sure that a potential click event is ignored. This is necessary for
/// [MouseEvent]s. We have to wait for and cancel a potential click event
/// happening after the mouseUp event.
void _suppressClickEvent() {
StreamSubscription clickPreventer =
_elementOrElementList.onClick.listen((event) {
void _suppressClickEvent(Element element) {
StreamSubscription clickPreventer = element.onClick.listen((event) {
event.stopPropagation();
event.preventDefault();
});
Expand All @@ -292,7 +296,6 @@ class Draggable {
// Then cancel the listener.
new Future(() {
clickPreventer.cancel();
clickPreventer = null;
});
}

Expand All @@ -302,9 +305,6 @@ class Draggable {
void destroy() {
_resetCurrentDrag();

// Reset the touch action property.
_elementOrElementList.style.touchAction = null;

// Destroy all managers with their listeners.
_eventManagers.forEach((m) => m.destroy());
_eventManagers.clear();
Expand Down
199 changes: 94 additions & 105 deletions lib/src/draggable_manager.dart
Expand Up @@ -2,8 +2,8 @@ part of dnd;

/// Class responsible for managing browser events.
///
/// This class is an abstraction for the specific managers like
/// [_TouchManager], [_MouseManager], etc.
/// This class is an abstraction for the specific managers
/// [_TouchManager], [_MouseManager], and [_PointerManager].
abstract class _EventManager {
/// Attribute to mark custom elements where events should be retargeted
/// to their Shadow DOM children.
Expand All @@ -27,13 +27,13 @@ abstract class _EventManager {
// horizontalOnly / verticalOnly options.
if (drg.horizontalOnly) {
// Only allow vertical scrolling, panning.
drg._elementOrElementList.style.touchAction = 'pan-y';
drg._elements.forEach((el) => el.style.touchAction = 'pan-y');
} else if (drg.verticalOnly) {
// Only allow horizontal scrolling, panning.
drg._elementOrElementList.style.touchAction = 'pan-x';
drg._elements.forEach((el) => el.style.touchAction = 'pan-x');
} else {
// No scrolling, panning.
drg._elementOrElementList.style.touchAction = 'none';
drg._elements.forEach((el) => el.style.touchAction = 'none');
}
}

Expand Down Expand Up @@ -132,6 +132,9 @@ abstract class _EventManager {
// Cancel start subscriptions.
startSubs.forEach((sub) => sub.cancel());
startSubs.clear();

// Reset the touch action property.
drg._elements.forEach((el) => el.style.touchAction = null);
}

/// Determine a target using `document.elementFromPoint` via the provided [clientPosition].
Expand Down Expand Up @@ -209,16 +212,8 @@ abstract class _EventManager {
}

// 2. The target must be a child of the drag element(s).
if (drg._elementOrElementList is ElementList) {
for (Element el in drg._elementOrElementList) {
if (el.contains(target)) {
return true;
}
}
} else {
if ((drg._elementOrElementList as Element).contains(target)) {
return true;
}
if (drg._elements.firstWhere((el) => el.contains(target)) != null) {
return true;
}
}

Expand All @@ -236,25 +231,26 @@ class _TouchManager extends _EventManager {

@override
void installStart() {
startSubs
.add(drg._elementOrElementList.onTouchStart.listen((TouchEvent event) {
// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}
drg._elements.forEach((el) {
startSubs.add(el.onTouchStart.listen((TouchEvent event) {
// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}

// Ignore multi-touch events.
if (event.touches.length > 1) {
return;
}
// Ignore multi-touch events.
if (event.touches.length > 1) {
return;
}

// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.touches[0].target)) {
return;
}
// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.touches[0].target)) {
return;
}

handleStart(event, event.touches[0].page);
}));
handleStart(event, event.touches[0].page);
}));
});
}

@override
Expand Down Expand Up @@ -323,42 +319,43 @@ class _MouseManager extends _EventManager {

@override
void installStart() {
startSubs
.add(drg._elementOrElementList.onMouseDown.listen((MouseEvent event) {
// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}
drg._elements.forEach((el) {
startSubs.add(el.onMouseDown.listen((MouseEvent event) {
// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}

// Only handle left clicks, ignore clicks from right or middle buttons.
if (event.button != 0) {
return;
}
// Only handle left clicks, ignore clicks from right or middle buttons.
if (event.button != 0) {
return;
}

// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.target)) {
return;
}
// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.target)) {
return;
}

// Prevent default on mouseDown. Reasons:
// * Disables image dragging handled by the browser.
// * Disables text selection.
//
// Note: We must NOT prevent default on form elements. Reasons:
// * SelectElement would not show a dropdown.
// * InputElement and TextAreaElement would not get focus.
// * ButtonElement and OptionElement - don't know if this is needed??
Element target = event.target;
if (!(target is SelectElement ||
target is InputElement ||
target is TextAreaElement ||
target is ButtonElement ||
target is OptionElement)) {
event.preventDefault();
}
// Prevent default on mouseDown. Reasons:
// * Disables image dragging handled by the browser.
// * Disables text selection.
//
// Note: We must NOT prevent default on form elements. Reasons:
// * SelectElement would not show a dropdown.
// * InputElement and TextAreaElement would not get focus.
// * ButtonElement and OptionElement - don't know if this is needed??
Element target = event.target;
if (!(target is SelectElement ||
target is InputElement ||
target is TextAreaElement ||
target is ButtonElement ||
target is OptionElement)) {
event.preventDefault();
}

handleStart(event, event.page);
}));
handleStart(event, event.page);
}));
});
}

@override
Expand Down Expand Up @@ -387,53 +384,45 @@ class _PointerManager extends _EventManager {

@override
void installStart() {
// The [ElementList] does not have the `on` method for custom events. So,
// we need to manually go trough all [Element]s and call the [installFunc].
if (drg._elementOrElementList is ElementList) {
drg._elementOrElementList.forEach(_doInstallStart);
} else {
_doInstallStart(drg._elementOrElementList);
}
}

void _doInstallStart(Element element) {
startSubs.add(element.on['pointerdown'].listen((e) {
var event = e as PointerEvent;
drg._elements.forEach((el) {
startSubs.add(el.on['pointerdown'].listen((e) {
var event = e as PointerEvent;

// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}
// Ignore if drag is already beeing handled.
if (_currentDrag != null) {
return;
}

// Only handle left clicks, ignore clicks from right or middle buttons.
if (event.button != 0) {
return;
}
// Only handle left clicks, ignore clicks from right or middle buttons.
if (event.button != 0) {
return;
}

// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.target)) {
return;
}
// Ensure the drag started on a valid target.
if (!_isValidDragStartTarget(event.target)) {
return;
}

// Prevent default on mouseDown. Reasons:
// * Disables image dragging handled by the browser.
// * Disables text selection.
//
// Note: We must NOT prevent default on form elements. Reasons:
// * SelectElement would not show a dropdown.
// * InputElement and TextAreaElement would not get focus.
// * ButtonElement and OptionElement - don't know if this is needed??
Element target = event.target;
if (!(target is SelectElement ||
target is InputElement ||
target is TextAreaElement ||
target is ButtonElement ||
target is OptionElement)) {
event.preventDefault();
}
// Prevent default on mouseDown. Reasons:
// * Disables image dragging handled by the browser.
// * Disables text selection.
//
// Note: We must NOT prevent default on form elements. Reasons:
// * SelectElement would not show a dropdown.
// * InputElement and TextAreaElement would not get focus.
// * ButtonElement and OptionElement - don't know if this is needed??
Element target = event.target;
if (!(target is SelectElement ||
target is InputElement ||
target is TextAreaElement ||
target is ButtonElement ||
target is OptionElement)) {
event.preventDefault();
}

handleStart(event, event.page);
}));
handleStart(event, event.page);
}));
});
}

@override
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
@@ -1,5 +1,5 @@
name: dnd
version: 1.1.0
version: 1.2.0
author: Marco Jakob <majakob@gmx.ch>
description: Drag and Drop for Dart web apps with mouse and touch support.
homepage: http://code.makery.ch/library/dart-drag-and-drop/
Expand Down

0 comments on commit b8578a0

Please sign in to comment.