Skip to content

Commit 65d804c

Browse files
committed
1.1.0
Update action-handler
1 parent fcb6fd8 commit 65d804c

File tree

4 files changed

+199
-158
lines changed

4 files changed

+199
-158
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "roku-card",
3-
"version": "1.0.9",
3+
"version": "1.1.0",
44
"description": "Lovelace Roku Card",
55
"keywords": [
66
"home-assistant",
@@ -16,7 +16,7 @@
1616
"author": "Ian Richardson <iantrich@gmail.com>",
1717
"license": "MIT",
1818
"dependencies": {
19-
"custom-card-helpers": "^1.3.9",
19+
"custom-card-helpers": "^1.6.3",
2020
"home-assistant-js-websocket": "^4.4.0",
2121
"lit-element": "^2.2.1",
2222
"lit-html": "^1.1.2"

src/action-handler-directive.ts

Lines changed: 49 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
import { directive, PropertyPart } from 'lit-html';
2-
import { fireEvent, ActionHandlerOptions } from 'custom-card-helpers';
2+
3+
import { fireEvent, ActionHandlerDetail, ActionHandlerOptions } from 'custom-card-helpers';
34

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

67
interface ActionHandler extends HTMLElement {
78
holdTime: number;
89
bind(element: Element, options): void;
910
}
10-
interface ActionHandlerElement extends Element {
11+
12+
interface ActionHandlerElement extends HTMLElement {
1113
actionHandler?: boolean;
1214
}
1315

16+
declare global {
17+
interface HASSDomEvents {
18+
action: ActionHandlerDetail;
19+
}
20+
}
21+
1422
class ActionHandler extends HTMLElement implements ActionHandler {
15-
public holdTime: number;
16-
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
23+
public holdTime = 500;
24+
1725
public ripple: any;
18-
protected timer: number | undefined;
19-
protected held: boolean;
20-
protected cooldownStart: boolean;
21-
protected cooldownEnd: boolean;
22-
private dblClickTimeout: number | undefined;
26+
27+
protected timer?: number;
28+
29+
protected held = false;
30+
31+
private dblClickTimeout?: number;
2332

2433
constructor() {
2534
super();
26-
this.holdTime = 500;
2735
this.ripple = document.createElement('mwc-ripple');
28-
this.timer = undefined;
29-
this.held = false;
30-
this.cooldownStart = false;
31-
this.cooldownEnd = false;
3236
}
3337

3438
public connectedCallback(): void {
@@ -38,6 +42,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
3842
height: isTouch ? '100px' : '50px',
3943
transform: 'translate(-50%, -50%)',
4044
pointerEvents: 'none',
45+
zIndex: '999',
4146
});
4247

4348
this.appendChild(this.ripple);
@@ -72,13 +77,10 @@ class ActionHandler extends HTMLElement implements ActionHandler {
7277
}
7378
e.cancelBubble = true;
7479
e.returnValue = false;
75-
return;
80+
return false;
7681
});
7782

78-
const clickStart = (ev: Event): void => {
79-
if (this.cooldownStart) {
80-
return;
81-
}
83+
const start = (ev: Event): void => {
8284
this.held = false;
8385
let x;
8486
let y;
@@ -94,56 +96,50 @@ class ActionHandler extends HTMLElement implements ActionHandler {
9496
this.startAnimation(x, y);
9597
this.held = true;
9698
}, this.holdTime);
97-
98-
this.cooldownStart = true;
99-
window.setTimeout(() => (this.cooldownStart = false), 100);
10099
};
101100

102-
const clickEnd = (ev: Event): void => {
103-
if (this.cooldownEnd || (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined)) {
101+
const end = (ev: Event): void => {
102+
// Prevent mouse event if touch event
103+
ev.preventDefault();
104+
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
104105
return;
105106
}
106107
clearTimeout(this.timer);
107108
this.stopAnimation();
108109
this.timer = undefined;
109110
if (this.held) {
110-
fireEvent(element as HTMLElement, 'action', { action: 'hold' });
111-
} else if (options.hasDoubleTap) {
112-
if ((ev as MouseEvent).detail === 1 || ev.type === 'keyup') {
111+
fireEvent(element, 'action', { action: 'hold' });
112+
} else if (options.hasDoubleClick) {
113+
if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
113114
this.dblClickTimeout = window.setTimeout(() => {
114-
fireEvent(element as HTMLElement, 'action', { action: 'tap' });
115+
this.dblClickTimeout = undefined;
116+
fireEvent(element, 'action', { action: 'tap' });
115117
}, 250);
116118
} else {
117119
clearTimeout(this.dblClickTimeout);
118-
fireEvent(element as HTMLElement, 'action', { action: 'double_tap' });
120+
this.dblClickTimeout = undefined;
121+
fireEvent(element, 'action', { action: 'double_tap' });
119122
}
120123
} else {
121-
fireEvent(element as HTMLElement, 'action', { action: 'tap' });
124+
fireEvent(element, 'action', { action: 'tap' });
122125
}
123-
this.cooldownEnd = true;
124-
window.setTimeout(() => (this.cooldownEnd = false), 100);
125126
};
126127

127-
const handleEnter = (ev: Event): void => {
128-
if ((ev as KeyboardEvent).keyCode === 13) {
129-
return clickEnd(ev);
128+
const handleEnter = (ev: KeyboardEvent): void => {
129+
if (ev.keyCode !== 13) {
130+
return;
130131
}
132+
end(ev);
131133
};
132134

133-
element.addEventListener('touchstart', clickStart, { passive: true });
134-
element.addEventListener('touchend', clickEnd);
135-
element.addEventListener('touchcancel', clickEnd);
136-
element.addEventListener('keyup', handleEnter);
135+
element.addEventListener('touchstart', start, { passive: true });
136+
element.addEventListener('touchend', end);
137+
element.addEventListener('touchcancel', end);
137138

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

149145
private startAnimation(x: number, y: number): void {
@@ -164,15 +160,15 @@ class ActionHandler extends HTMLElement implements ActionHandler {
164160
}
165161
}
166162

167-
customElements.define('action-handler-roku', ActionHandler);
163+
customElements.define('action-handler-roku-card', ActionHandler);
168164

169165
const getActionHandler = (): ActionHandler => {
170166
const body = document.body;
171-
if (body.querySelector('action-handler-roku')) {
172-
return body.querySelector('action-handler-roku') as ActionHandler;
167+
if (body.querySelector('action-handler-roku-card')) {
168+
return body.querySelector('action-handler-roku-card') as ActionHandler;
173169
}
174170

175-
const actionhandler = document.createElement('action-handler-roku');
171+
const actionhandler = document.createElement('action-handler-roku-card');
176172
body.appendChild(actionhandler);
177173

178174
return actionhandler as ActionHandler;
@@ -187,5 +183,5 @@ export const actionHandlerBind = (element: ActionHandlerElement, options: Action
187183
};
188184

189185
export const actionHandler = directive((options: ActionHandlerOptions = {}) => (part: PropertyPart): void => {
190-
actionHandlerBind(part.committer.element, options);
186+
actionHandlerBind(part.committer.element as ActionHandlerElement, options);
191187
});

src/roku-card.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ export class RokuCard extends LitElement {
162162
@action=${this._handleAction}
163163
.actionHandler=${actionHandler({
164164
hasHold: hasAction(this._config.apps[index].hold_action),
165-
hasDoubleTap: hasAction(this._config.apps[index].double_tap_action),
166-
repeat: this._config.apps[index].hold_action ? this._config.apps[index].hold_action!.repeat : undefined,
165+
hasDoubleClick: hasAction(this._config.apps[index].double_tap_action),
167166
})}
168167
/>
169168
`
@@ -187,8 +186,7 @@ export class RokuCard extends LitElement {
187186
@action=${this._handleAction}
188187
.actionHandler=${actionHandler({
189188
hasHold: config && hasAction(config.hold_action),
190-
hasDoubleTap: config && hasAction(config.double_tap_action),
191-
repeat: config && config.hold_action ? config.hold_action.repeat : undefined,
189+
hasDoubleClick: config && hasAction(config.double_tap_action),
192190
})}
193191
></paper-icon-button>
194192
`;

0 commit comments

Comments
 (0)