11import { directive , PropertyPart } from 'lit-html' ;
2- import { fireEvent , ActionHandlerOptions } from 'custom-card-helpers' ;
2+
3+ import { fireEvent , ActionHandlerDetail , ActionHandlerOptions } from 'custom-card-helpers' ;
34
45const isTouch = 'ontouchstart' in window || navigator . maxTouchPoints > 0 || navigator . msMaxTouchPoints > 0 ;
56
67interface 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+
1422class 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 = / i P h o n e O S 1 3 _ / . 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
169165const 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
189185export const actionHandler = directive ( ( options : ActionHandlerOptions = { } ) => ( part : PropertyPart ) : void => {
190- actionHandlerBind ( part . committer . element , options ) ;
186+ actionHandlerBind ( part . committer . element as ActionHandlerElement , options ) ;
191187} ) ;
0 commit comments