Skip to content

Commit

Permalink
fix(ripple)!: rename press methods to event handlers
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 511251253
  • Loading branch information
asyncLiz authored and copybara-github committed Feb 21, 2023
1 parent 43ce8c1 commit 0cc7d29
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 177 deletions.
4 changes: 2 additions & 2 deletions chips/action/lib/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ export abstract class Action extends ActionElement {
}

override beginPress({positionEvent}: BeginPressConfig) {
this.ripple?.beginPress(positionEvent);
// TODO(b/253297063): connect to ripple
}

override endPress(options: EndPressConfig) {
super.endPress(options);
this.ripple?.endPress();
// TODO(b/253297063): connect to ripple
if (!options.cancelled) {
this.dispatchCustomEvent(this.getInteractionEvent());
}
Expand Down
2 changes: 1 addition & 1 deletion chips/action/lib/link-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ export class LinkAction extends PrimaryAction {

override endPress(options: EndPressConfig) {
super.endPress(options);
this.ripple?.endPress();
// TODO(b/253297063): connect to ripple
}
}
4 changes: 2 additions & 2 deletions navigationtab/lib/navigation-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ export class NavigationTab extends ActionElement implements NavigationTabState {
}

override beginPress({positionEvent}: BeginPressConfig) {
this.ripple.beginPress(positionEvent);
// TODO(b/269772145): connect to ripple
}

override endPress(options: EndPressConfig) {
this.ripple.endPress();
// TODO(b/269772145): connect to ripple
super.endPress(options);
if (!options.cancelled) {
this.dispatchEvent(new CustomEvent(
Expand Down
155 changes: 7 additions & 148 deletions ripple/directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@
import {noChange} from 'lit';
import {Directive, directive, DirectiveParameters, ElementPart, PartInfo, PartType} from 'lit/directive.js';

import {Ripple, State} from './lib/ripple.js';

/**
* Delay reacting to touch so that we do not show the ripple for a swipe or
* scroll interaction.
*/
const TOUCH_DELAY_MS = 150;
import {Ripple} from './lib/ripple.js';

/**
* Normalized ripple accessor type.
Expand All @@ -25,9 +19,6 @@ type RippleFunction = () => Ripple|null|Promise<Ripple|null>;
class RippleDirective extends Directive {
private rippleGetter: RippleFunction = async () => null;
private element?: HTMLElement;
private checkBoundsAfterContextMenu = false;
private rippleStartEvent: PointerEvent|null = null;
private touchTimer: number|null = null;

constructor(partInfo: PartInfo) {
super(partInfo);
Expand All @@ -49,25 +40,25 @@ class RippleDirective extends Directive {
}
switch (event.type) {
case 'click':
this.click(ripple);
ripple.handleClick();
break;
case 'contextmenu':
this.contextMenu(ripple);
ripple.handleContextmenu();
break;
case 'pointercancel':
this.pointerCancel(ripple, event as PointerEvent);
ripple.handlePointercancel(event as PointerEvent);
break;
case 'pointerdown':
this.pointerDown(ripple, event as PointerEvent);
await ripple.handlePointerdown(event as PointerEvent);
break;
case 'pointerenter':
ripple.handlePointerenter(event as PointerEvent);
break;
case 'pointerleave':
this.pointerLeave(ripple, event as PointerEvent);
ripple.handlePointerleave(event as PointerEvent);
break;
case 'pointerup':
this.pointerUp(ripple, event as PointerEvent);
ripple.handlePointerup(event as PointerEvent);
break;
default:
break;
Expand All @@ -91,138 +82,6 @@ class RippleDirective extends Directive {
this.rippleGetter = typeof ripple === 'function' ? ripple : () => ripple;
return noChange;
}

/**
* Returns `true` if
* - the ripple element is enabled
* - the pointer is primary for the input type
* - the pointer is the pointer that started the interaction, or will start
* the interaction
* - the pointer is a touch, or the pointer state has the primary button
* held, or the pointer is hovering
*/
private shouldReactToEvent(
ripple: Ripple, ev: PointerEvent, hovering = false) {
const enabled = !ripple.disabled;
const isPrimaryPointer = ev.isPrimary;
const isInteractionPointer = this.rippleStartEvent === null ||
this.rippleStartEvent.pointerId === ev.pointerId;
const isPrimaryButton = ev.buttons === 1;
return enabled && isPrimaryPointer && isInteractionPointer &&
(this.isTouch(ev) || isPrimaryButton || hovering);
}

private isTouch({pointerType}: PointerEvent) {
return pointerType === 'touch';
}

/**
* Check if the event is within the bounds of the element.
*
* This is only needed for the "stuck" contextmenu longpress on Chrome.
*/
private inBounds({x, y}: PointerEvent) {
const {top, left, bottom, right} = this.element!.getBoundingClientRect();
return x >= left && x <= right && y >= top && y <= bottom;
}

private beginPress(ripple: Ripple) {
ripple.beginPress(this.rippleStartEvent);
}

private endPress(ripple: Ripple) {
ripple.endPress();
ripple.state = State.INACTIVE;
this.rippleStartEvent = null;
if (this.touchTimer) {
clearTimeout(this.touchTimer);
this.touchTimer = null;
}
}

private waitForTouchHold(ripple: Ripple) {
if (this.touchTimer !== null) {
clearTimeout(this.touchTimer);
}
ripple.state = State.TOUCH_DELAY;
this.touchTimer = setTimeout(() => {
if (ripple.state !== State.TOUCH_DELAY) {
return;
}
ripple.state = State.HOLDING;
this.beginPress(ripple);
}, TOUCH_DELAY_MS);
}

private click(ripple: Ripple) {
// Click is a MouseEvent in Firefox and Safari, so we cannot use
// `shouldReactToEvent`
if (ripple.disabled) {
return;
}
if (ripple.state === State.WAITING_FOR_CLICK) {
this.endPress(ripple);
} else if (ripple.state === State.INACTIVE) {
// keyboard synthesized click event
this.beginPress(ripple);
this.endPress(ripple);
}
}

private contextMenu(ripple: Ripple) {
if (!ripple.disabled) {
this.checkBoundsAfterContextMenu = true;
this.endPress(ripple);
}
}

private pointerDown(ripple: Ripple, ev: PointerEvent) {
if (!this.shouldReactToEvent(ripple, ev)) {
return;
}
this.rippleStartEvent = ev;
if (this.isTouch(ev)) {
// after a longpress contextmenu event, an extra `pointerdown` can be
// dispatched to the pressed element. Check that the down is within
// bounds of the element in this case.
if (this.checkBoundsAfterContextMenu && !this.inBounds(ev)) {
return;
}
this.checkBoundsAfterContextMenu = false;
this.waitForTouchHold(ripple);
} else {
ripple.state = State.WAITING_FOR_CLICK;
this.beginPress(ripple);
}
}

private pointerUp(ripple: Ripple, ev: PointerEvent) {
if (!this.isTouch(ev) || !this.shouldReactToEvent(ripple, ev)) {
return;
}
if (ripple.state === State.HOLDING) {
ripple.state = State.WAITING_FOR_CLICK;
} else if (ripple.state === State.TOUCH_DELAY) {
ripple.state = State.WAITING_FOR_CLICK;
this.beginPress(ripple);
}
}

private pointerCancel(ripple: Ripple, ev: PointerEvent) {
if (this.shouldReactToEvent(ripple, ev)) {
this.endPress(ripple);
}
}

private pointerLeave(ripple: Ripple, ev: PointerEvent) {
if (this.shouldReactToEvent(ripple, ev, true)) {
ripple.handlePointerleave(ev);
// release a held mouse or pen press that moves outside the element
if (!this.isTouch(ev) && ripple.state !== State.INACTIVE) {
this.endPress(ripple);
}
}
}
}

/**
Expand Down
Loading

0 comments on commit 0cc7d29

Please sign in to comment.