Parcourir la source

DateClicking/DateDragging

Adam Shaw il y a 8 ans
Parent
commit
8670fa5a33

+ 53 - 6
src/common/CoordChronoComponentMixin.day-click.js

@@ -1,12 +1,59 @@
 
-$.extend(CoordChronoComponentMixin, {
+var DateClicking = Class.extend({
+
+	view: null,
+	component: null, // CoordComponent
+	dragListener: null,
+
+
+	/*
+	component must implement:
+		- bindDayHandler
+		- registerDragListener
+		- getSafeHitFootprint
+		- getHitEl
+	*/
+	constructor: function(component) {
+		this.view = component.view;
+		this.component = component;
+		this.dragListener = this.buildDragListener();
+
+		this.bind();
+	},
+
+
+	opt: function(name) {
+		return this.view.opt(name);
+	},
+
+
+	bind: function() {
+		var component = this.component;
+		var dragListener = this.dragListener;
+
+		component.bindDayHandler('mousedown', function(ev) {
+			if (!component.shouldIgnoreMouse()) {
+				dragListener.startInteraction(ev);
+			}
+		});
+
+		component.bindDayHandler('touchstart', function(ev) {
+			if (!component.shouldIgnoreTouch()) {
+				dragListener.startInteraction(ev);
+			}
+		});
+
+		component.registerDragListener(dragListener);
+	},
+
 
 	// Creates a listener that tracks the user's drag across day elements, for day clicking.
-	buildDayClickListener: function() {
+	buildDragListener: function() {
 		var _this = this;
+		var component = this.component;
 		var dayClickHit; // null if invalid dayClick
 
-		var dragListener = new HitDragListener(this, {
+		var dragListener = new HitDragListener(component, {
 			scroll: this.opt('dragScroll'),
 			interactionStart: function() {
 				dayClickHit = dragListener.origHit;
@@ -24,16 +71,16 @@ $.extend(CoordChronoComponentMixin, {
 				var componentFootprint;
 
 				if (!isCancelled && dayClickHit) {
-					componentFootprint = _this.getSafeHitFootprint(dayClickHit);
+					componentFootprint = component.getSafeHitFootprint(dayClickHit);
 
 					if (componentFootprint) {
-						_this.view.triggerDayClick(componentFootprint, _this.getHitEl(dayClickHit), ev);
+						_this.view.triggerDayClick(componentFootprint, component.getHitEl(dayClickHit), ev);
 					}
 				}
 			}
 		});
 
-		// because dayClickListener won't be called with any time delay, "dragging" will begin immediately,
+		// because dragListener won't be called with any time delay, "dragging" will begin immediately,
 		// which will kill any touchmoving/scrolling. Prevent this.
 		dragListener.shouldCancelTouchScroll = false;
 

+ 73 - 20
src/common/CoordChronoComponentMixin.day-selection.js

@@ -1,12 +1,78 @@
 
-$.extend(CoordChronoComponentMixin, {
+var DateSelecting = Class.extend({
+
+	view: null,
+	component: null, // CoordComponent
+	dragListener: null,
+
+
+	/*
+	component must implement:
+		- bindDayHandler
+		- registerDragListener
+		- getSafeHitFootprint
+		- renderHighlight
+		- unrenderHighlight
+	*/
+	constructor: function(component) {
+		this.component = component;
+		this.view = component.view;
+		this.dragListener = this.buildDragListener();
+
+		this.bind();
+	},
+
+
+	opt: function(name) {
+		return this.view.opt(name);
+	},
+
+
+	getDelay: function() {
+		var delay = this.opt('selectLongPressDelay');
+
+		if (delay == null) {
+			delay = this.opt('longPressDelay'); // fallback
+		}
+
+		return delay;
+	},
+
+
+	bind: function() {
+		var _this = this;
+		var component = this.component;
+		var dragListener = this.dragListener;
+
+		component.bindDayHandler('mousedown', function(ev) {
+			if (_this.opt('selectable') && !component.shouldIgnoreMouse()) {
+				dragListener.startInteraction(ev, {
+					distance: _this.opt('selectMinDistance')
+				});
+			}
+		});
+
+		component.bindDayHandler('touchstart', function(ev) {
+			if (_this.opt('selectable') && !component.shouldIgnoreTouch()) {
+				dragListener.startInteraction(ev, {
+					delay: _this.getDelay()
+				});
+			}
+		});
+
+		preventSelection(component.el);
+
+		component.registerDragListener(dragListener);
+	},
+
 
 	// Creates a listener that tracks the user's drag across day elements, for day selecting.
-	buildDaySelectListener: function() {
+	buildDragListener: function() {
 		var _this = this;
+		var component = this.component;
 		var selectionFootprint; // null if invalid selection
 
-		var dragListener = new HitDragListener(this, {
+		var dragListener = new HitDragListener(component, {
 			scroll: this.opt('dragScroll'),
 			interactionStart: function() {
 				selectionFootprint = null;
@@ -20,8 +86,8 @@ $.extend(CoordChronoComponentMixin, {
 
 				if (origHit) { // click needs to have started on a hit
 
-					origHitFootprint = _this.getSafeHitFootprint(origHit);
-					hitFootprint = _this.getSafeHitFootprint(hit);
+					origHitFootprint = component.getSafeHitFootprint(origHit);
+					hitFootprint = component.getSafeHitFootprint(hit);
 
 					if (origHitFootprint && hitFootprint) {
 						selectionFootprint = _this.computeSelection(origHitFootprint, hitFootprint);
@@ -31,7 +97,7 @@ $.extend(CoordChronoComponentMixin, {
 					}
 
 					if (selectionFootprint) {
-						_this.renderSelectionFootprint(selectionFootprint);
+						component.renderSelectionFootprint(selectionFootprint);
 					}
 					else if (selectionFootprint === false) {
 						disableCursor();
@@ -40,7 +106,7 @@ $.extend(CoordChronoComponentMixin, {
 			},
 			hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
 				selectionFootprint = null;
-				_this.unrenderSelection();
+				component.unrenderSelection();
 			},
 			hitDone: function() { // called after a hitOut OR before a dragEnd
 				enableCursor();
@@ -57,19 +123,6 @@ $.extend(CoordChronoComponentMixin, {
 	},
 
 
-	// Renders a visual indication of a selection. Will highlight by default but can be overridden by subclasses.
-	// Given a span (unzoned start/end and other misc data)
-	renderSelectionFootprint: function(componentFootprint) {
-		this.renderHighlight(componentFootprint);
-	},
-
-
-	// Unrenders any visual indications of a selection. Will unrender a highlight by default.
-	unrenderSelection: function() {
-		this.unrenderHighlight();
-	},
-
-
 	// Given the first and last date-spans of a selection, returns another date-span object.
 	// Subclasses can override and provide additional data in the span object. Will be passed to renderSelectionFootprint().
 	// Will return false if the selection is invalid and this should be indicated to the user.

+ 43 - 47
src/common/CoordChronoComponentMixin.js

@@ -11,8 +11,6 @@ var CoordChronoComponentMixin = {
 	// TODO: port isTimeScale into same system?
 	largeUnit: null,
 
-	dayClickListener: null,
-	daySelectListener: null,
 	segDragListener: null,
 	segResizeListener: null,
 	externalDragListener: null,
@@ -22,10 +20,11 @@ var CoordChronoComponentMixin = {
 	// self-config, overridable by subclasses
 	hasDayInteractions: true, // can user click/select ranges of time?
 
+	dragListeners: null,
+
 
 	initCoordChronoComponent: function() {
-		this.dayClickListener = this.buildDayClickListener();
-		this.daySelectListener = this.buildDaySelectListener();
+		this.dragListeners = [];
 	},
 
 
@@ -45,9 +44,14 @@ var CoordChronoComponentMixin = {
 	// Useful for when public API methods that result in re-rendering are invoked during a drag.
 	// Also useful for when touch devices misbehave and don't fire their touchend.
 	clearDragListeners: function() {
-		this.dayClickListener.endInteraction();
-		this.daySelectListener.endInteraction();
+		var dragListeners = this.dragListeners;
+		var i;
 
+		for (i = 0; i < dragListeners.length; i++) {
+			dragListeners[i].endInteraction();
+		}
+
+		// TODO: get these to start using registerDragListener()
 		if (this.segDragListener) {
 			this.segDragListener.endInteraction(); // will clear this.segDragListener
 		}
@@ -70,10 +74,8 @@ var CoordChronoComponentMixin = {
 		ChronoComponent.prototype.setElement.apply(this, arguments);
 
 		if (this.hasDayInteractions) {
-			preventSelection(el);
-
-			this.bindDayHandler('touchstart', this.dayTouchStart);
-			this.bindDayHandler('mousedown', this.dayMousedown);
+			new DateClicking(this);
+			new DateSelecting(this);
 		}
 
 		// attach event-element-related handlers. in Grid.events
@@ -150,9 +152,11 @@ var CoordChronoComponentMixin = {
 	// Attaches event-element-related handlers to an arbitrary container element. leverages bubbling.
 	bindSegHandlersToEl: function(el) {
 		this.bindSegHandlerToEl(el, 'touchstart', this.handleSegTouchStart);
+		this.bindSegHandlerToEl(el, 'mousedown', this.handleSegMousedown);
+
+		// nothing to do with "coord"component
 		this.bindSegHandlerToEl(el, 'mouseenter', this.handleSegMouseover);
 		this.bindSegHandlerToEl(el, 'mouseleave', this.handleSegMouseout);
-		this.bindSegHandlerToEl(el, 'mousedown', this.handleSegMousedown);
 		this.bindSegHandlerToEl(el, 'click', this.handleSegClick);
 	},
 
@@ -173,56 +177,31 @@ var CoordChronoComponentMixin = {
 	},
 
 
-	/* Handlers
-	------------------------------------------------------------------------------------------------------------------*/
-
-
-	// Process a mousedown on an element that represents a day. For day clicking and selecting.
-	dayMousedown: function(ev) {
-
+	shouldIgnoreMouse: function() {
 		// HACK
 		// This will still work even though bindDayHandler doesn't use GlobalEmitter.
-		if (GlobalEmitter.get().shouldIgnoreMouse()) {
-			return;
-		}
-
-		this.dayClickListener.startInteraction(ev);
-
-		if (this.opt('selectable')) {
-			this.daySelectListener.startInteraction(ev, {
-				distance: this.opt('selectMinDistance')
-			});
-		}
+		return GlobalEmitter.get().shouldIgnoreMouse();
 	},
 
 
-	dayTouchStart: function(ev) {
-		var view = this._getView();
-		var selectLongPressDelay;
-
+	shouldIgnoreTouch: function() {
 		// On iOS (and Android?) when a new selection is initiated overtop another selection,
 		// the touchend never fires because the elements gets removed mid-touch-interaction (my theory).
 		// HACK: simply don't allow this to happen.
 		// ALSO: prevent selection when an *event* is already raised.
-		if (view.isSelected || view.selectedEvent) {
-			return;
-		}
-
-		selectLongPressDelay = this.opt('selectLongPressDelay');
-		if (selectLongPressDelay == null) {
-			selectLongPressDelay = this.opt('longPressDelay'); // fallback
-		}
+		return this.view.isSelected || this.view.selectedEvent;
+	},
 
-		this.dayClickListener.startInteraction(ev);
 
-		if (this.opt('selectable')) {
-			this.daySelectListener.startInteraction(ev, {
-				delay: selectLongPressDelay
-			});
-		}
+	registerDragListener: function(dragListener) {
+		this.dragListeners.push(dragListener);
 	},
 
 
+	/* Handlers
+	------------------------------------------------------------------------------------------------------------------*/
+
+
 	handleSegClick: function(seg, ev) {
 		var view = this._getView();
 		var res = this.publiclyTrigger('eventClick', { // can return `false` to cancel
@@ -420,6 +399,23 @@ var CoordChronoComponentMixin = {
 	},
 
 
+	/* Selection
+	------------------------------------------------------------------------------------------------------------------*/
+
+
+	// Renders a visual indication of a selection. Will highlight by default but can be overridden by subclasses.
+	// Given a span (unzoned start/end and other misc data)
+	renderSelectionFootprint: function(componentFootprint) {
+		this.renderHighlight(componentFootprint);
+	},
+
+
+	// Unrenders any visual indications of a selection. Will unrender a highlight by default.
+	unrenderSelection: function() {
+		this.unrenderHighlight();
+	},
+
+
 	/* Hit Area
 	------------------------------------------------------------------------------------------------------------------*/
 

+ 1 - 0
src/common/SegChronoComponentMixin.js

@@ -63,6 +63,7 @@ var SegChronoComponentMixin = {
 	// Unrenders all events currently rendered on the grid
 	unrenderEvents: function() {
 		this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
+		// ^ part of CoordChronoComponent :(
 
 		this.unrenderFgSegs();
 		this.unrenderBgSegs();