Skip to content

Commit

Permalink
1.5.1
Browse files Browse the repository at this point in the history
Update action-handler
  • Loading branch information
iantrich committed May 3, 2020
1 parent 7f481e3 commit eec1af3
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 161 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "radial-menu",
"version": "1.5.0",
"version": "1.5.1",
"description": "Lovelace radial-menu",
"keywords": [
"home-assistant",
Expand All @@ -15,7 +15,7 @@
"author": "Ian Richardson <[email protected]>",
"license": "MIT",
"dependencies": {
"custom-card-helpers": "^1.3.9",
"custom-card-helpers": "^1.6.4",
"home-assistant-js-websocket": "^4.4.0",
"lit-element": "^2.2.1",
"lit-html": "^1.1.2"
Expand Down
93 changes: 44 additions & 49 deletions src/action-handler-directive.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
import { directive, PropertyPart } from 'lit-html';
import { fireEvent, ActionHandlerOptions } from 'custom-card-helpers';

import { fireEvent, ActionHandlerDetail, ActionHandlerOptions } from 'custom-card-helpers';

const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;

interface ActionHandler extends HTMLElement {
holdTime: number;
bind(element: Element, options): void;
}
interface ActionHandlerElement extends Element {
interface ActionHandlerElement extends HTMLElement {
actionHandler?: boolean;
}

declare global {
interface HASSDomEvents {
action: ActionHandlerDetail;
}
}

class ActionHandler extends HTMLElement implements ActionHandler {
public holdTime: number;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
public holdTime = 500;

public ripple: any;
protected timer: number | undefined;
protected held: boolean;
protected cooldownStart: boolean;
protected cooldownEnd: boolean;
private dblClickTimeout: number | undefined;

protected timer?: number;

protected held = false;

private dblClickTimeout?: number;

constructor() {
super();
this.holdTime = 500;
this.ripple = document.createElement('mwc-ripple');
this.timer = undefined;
this.held = false;
this.cooldownStart = false;
this.cooldownEnd = false;
}

public connectedCallback(): void {
Expand All @@ -38,6 +41,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
height: isTouch ? '100px' : '50px',
transform: 'translate(-50%, -50%)',
pointerEvents: 'none',
zIndex: '999',
});

this.appendChild(this.ripple);
Expand Down Expand Up @@ -72,13 +76,10 @@ class ActionHandler extends HTMLElement implements ActionHandler {
}
e.cancelBubble = true;
e.returnValue = false;
return;
return false;
});

const clickStart = (ev: Event): void => {
if (this.cooldownStart) {
return;
}
const start = (ev: Event): void => {
this.held = false;
let x;
let y;
Expand All @@ -94,56 +95,50 @@ class ActionHandler extends HTMLElement implements ActionHandler {
this.startAnimation(x, y);
this.held = true;
}, this.holdTime);

this.cooldownStart = true;
window.setTimeout(() => (this.cooldownStart = false), 100);
};

const clickEnd = (ev: Event): void => {
if (this.cooldownEnd || (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined)) {
const end = (ev: Event): void => {
// Prevent mouse event if touch event
ev.preventDefault();
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
return;
}
clearTimeout(this.timer);
this.stopAnimation();
this.timer = undefined;
if (this.held) {
fireEvent(element as HTMLElement, 'action', { action: 'hold' });
} else if (options.hasDoubleTap) {
if ((ev as MouseEvent).detail === 1 || ev.type === 'keyup') {
fireEvent(element, 'action', { action: 'hold' });
} else if (options.hasDoubleClick) {
if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
this.dblClickTimeout = window.setTimeout(() => {
fireEvent(element as HTMLElement, 'action', { action: 'tap' });
this.dblClickTimeout = undefined;
fireEvent(element, 'action', { action: 'tap' });
}, 250);
} else {
clearTimeout(this.dblClickTimeout);
fireEvent(element as HTMLElement, 'action', { action: 'double_tap' });
this.dblClickTimeout = undefined;
fireEvent(element, 'action', { action: 'double_tap' });
}
} else {
fireEvent(element as HTMLElement, 'action', { action: 'tap' });
fireEvent(element, 'action', { action: 'tap' });
}
this.cooldownEnd = true;
window.setTimeout(() => (this.cooldownEnd = false), 100);
};

const handleEnter = (ev: Event): void => {
if ((ev as KeyboardEvent).keyCode === 13) {
return clickEnd(ev);
const handleEnter = (ev: KeyboardEvent): void => {
if (ev.keyCode !== 13) {
return;
}
end(ev);
};

element.addEventListener('touchstart', clickStart, { passive: true });
element.addEventListener('touchend', clickEnd);
element.addEventListener('touchcancel', clickEnd);
element.addEventListener('keyup', handleEnter);
element.addEventListener('touchstart', start, { passive: true });
element.addEventListener('touchend', end);
element.addEventListener('touchcancel', end);

// iOS 13 sends a complete normal touchstart-touchend series of events followed by a mousedown-click series.
// That might be a bug, but until it's fixed, this should make action-handler work.
// If it's not a bug that is fixed, this might need updating with the next iOS version.
// Note that all events (both touch and mouse) must be listened for in order to work on computers with both mouse and touchscreen.
const isIOS13 = /iPhone OS 13_/.test(window.navigator.userAgent);
if (!isIOS13) {
element.addEventListener('mousedown', clickStart, { passive: true });
element.addEventListener('click', clickEnd);
}
element.addEventListener('mousedown', start, { passive: true });
element.addEventListener('click', end);

element.addEventListener('keyup', handleEnter);
}

private startAnimation(x: number, y: number): void {
Expand Down Expand Up @@ -187,5 +182,5 @@ export const actionHandlerBind = (element: ActionHandlerElement, options: Action
};

export const actionHandler = directive((options: ActionHandlerOptions = {}) => (part: PropertyPart): void => {
actionHandlerBind(part.committer.element, options);
actionHandlerBind(part.committer.element as ActionHandlerElement, options);
});
2 changes: 1 addition & 1 deletion src/const.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const CARD_VERSION = '1.5.0';
export const CARD_VERSION = '1.5.1';
8 changes: 4 additions & 4 deletions src/radial-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class RadialMenu extends LitElement {
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(item.hold_action),
hasDoubleTap: hasAction(item.double_tap_action),
hasDoubleClick: hasAction(item.double_tap_action),
})}
.config=${item}
.stateObj=${{
Expand All @@ -113,7 +113,7 @@ export class RadialMenu extends LitElement {
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(item.hold_action),
hasDoubleTap: hasAction(item.double_tap_action),
hasDoubleClick: hasAction(item.double_tap_action),
})}
.config=${item}
.icon=${item.icon}
Expand All @@ -133,7 +133,7 @@ export class RadialMenu extends LitElement {
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config.hold_action),
hasDoubleTap: hasAction(this._config.double_tap_action),
hasDoubleClick: hasAction(this._config.double_tap_action),
})}
.config=${this._config}
.stateObj=${{
Expand All @@ -151,7 +151,7 @@ export class RadialMenu extends LitElement {
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config.hold_action),
hasDoubleTap: hasAction(this._config.double_tap_action),
hasDoubleClick: hasAction(this._config.double_tap_action),
})}
.icon=${this._config.icon}
.title=${this._config.name}
Expand Down
Loading

0 comments on commit eec1af3

Please sign in to comment.