Skip to content

Commit

Permalink
feat(ripple): Implement subset of improved interaction response guide…
Browse files Browse the repository at this point in the history
…lines for ripple

Resolves #190
  • Loading branch information
cristobalchao@google.com committed Mar 1, 2017
1 parent 465a674 commit d65cf1b
Showing 1 changed file with 26 additions and 6 deletions.
32 changes: 26 additions & 6 deletions packages/mdc-ripple/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const DEACTIVATION_ACTIVATION_PAIRS = {
blur: 'focus',
};

const DEACTIVATION_TIMEOUT_MS = 200;

export default class MDCRippleFoundation extends MDCFoundation {
static get cssClasses() {
return cssClasses;
Expand Down Expand Up @@ -146,12 +148,20 @@ export default class MDCRippleFoundation extends MDCFoundation {

activationState.isActivated = true;
activationState.isProgrammatic = e === null;
activationState.isTimeoutDeactivated = false;
activationState.activationEvent = e;
activationState.wasActivatedByPointer = activationState.isProgrammatic ? false : (
e.type === 'mousedown' || e.type === 'touchstart' || e.type === 'pointerdown'
);

activationState.activationStartTime = Date.now();

activationState.deactivationTimeout = setTimeout(() => {
activationState.isTimeoutDeactivated = true;
this.deactivate_(e);
activationState.isActivated = false;
}, DEACTIVATION_TIMEOUT_MS);

requestAnimationFrame(() => {
// This needs to be wrapped in an rAF call b/c web browsers
// report active states inconsistently when they're called within
Expand Down Expand Up @@ -215,16 +225,18 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.activationState_ = this.defaultActivationState_();
return;
}

const actualActivationType = DEACTIVATION_ACTIVATION_PAIRS[e.type];
const expectedActivationType = activationState.activationEvent.type;
// NOTE: Pointer events are tricky - https://patrickhlauke.github.io/touch/tests/results/
// Essentially, what we need to do here is decouple the deactivation UX from the actual
// deactivation state itself. This way, touch/pointer events in sequence do not trample one
// another.
const needsDeactivationUX = actualActivationType === expectedActivationType;
const needsDeactivationUX = actualActivationType === expectedActivationType ||
activationState.isTimeoutDeactivated;
let needsActualDeactivation = needsDeactivationUX;
if (activationState.wasActivatedByPointer) {
needsActualDeactivation = e.type === 'mouseup';
needsActualDeactivation = activationState.isTimeoutDeactivated || e.type === 'mouseup';
}

const state = Object.assign({}, activationState);
Expand All @@ -234,22 +246,30 @@ export default class MDCRippleFoundation extends MDCFoundation {
if (needsActualDeactivation) {
this.activationState_ = this.defaultActivationState_();
}

clearTimeout(activationState.deactivationTimeout);
}

deactivate() {
this.deactivate_(null);
}

animateDeactivation_(e, {wasActivatedByPointer, wasElementMadeActive, activationStartTime, isProgrammatic}) {
animateDeactivation_(e, {wasActivatedByPointer, wasElementMadeActive, activationStartTime, isProgrammatic,
isTimeoutDeactivated}) {
const {BG_ACTIVE} = MDCRippleFoundation.cssClasses;
if (wasActivatedByPointer || wasElementMadeActive) {
this.adapter_.removeClass(BG_ACTIVE);
const isPointerEvent = isProgrammatic ? false : (
e.type === 'touchend' || e.type === 'pointerup' || e.type === 'mouseup'
);


if (this.adapter_.isUnbounded()) {
this.animateUnboundedDeactivation_(this.getUnboundedDeactivationInfo_(activationStartTime));
} else {
const isPointerEventOnDeactivation = isTimeoutDeactivated &&
(e.type === 'touchstart' || e.type === 'pointerdown' || e.type === 'mousedown');

const isPointerEvent = isProgrammatic ? false : (
e.type === 'touchend' || e.type === 'pointerup' || e.type === 'mouseup' || isPointerEventOnDeactivation
);
this.animateBoundedDeactivation_(e, isPointerEvent);
}
}
Expand Down

0 comments on commit d65cf1b

Please sign in to comment.