generated from custom-cards/boilerplate-card
-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,180 additions
and
1,360 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,27 @@ | ||
module.exports = { | ||
parser: "@typescript-eslint/parser", // Specifies the ESLint parser | ||
parserOptions: { | ||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features | ||
sourceType: "module" // Allows for the use of imports | ||
"env": { | ||
"browser": true, | ||
"es2021": true | ||
}, | ||
extends: [ | ||
"plugin:@typescript-eslint/recommended" // Uses the recommended rules from the @typescript-eslint/eslint-plugin | ||
"extends": "standard-with-typescript", | ||
"overrides": [ | ||
{ | ||
"env": { | ||
"node": true | ||
}, | ||
"files": [ | ||
".eslintrc.{js,cjs}" | ||
], | ||
"parserOptions": { | ||
"sourceType": "script" | ||
} | ||
} | ||
], | ||
rules: { | ||
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs | ||
// e.g. "@typescript-eslint/explicit-function-return-type": "off", | ||
"parserOptions": { | ||
"ecmaVersion": "latest", | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"@typescript-eslint/strict-boolean-expressions": "off" | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,20 @@ | ||
name: 'Build' | ||
name: yarn | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
build: | ||
name: Test build | ||
name: yarn | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: 14 | ||
node-version: 18 | ||
- name: Build | ||
run: | | ||
yarn install | ||
yarn build | ||
- name: Lint | ||
run: | | ||
yarn lint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,197 +1,196 @@ | ||
import { noChange } from 'lit'; | ||
import { AttributePart, directive, Directive, DirectiveParameters } from 'lit/directive'; | ||
import { noChange } from 'lit' | ||
import { type AttributePart, directive, Directive, type DirectiveParameters } from 'lit/directive' | ||
|
||
import { ActionHandlerDetail, ActionHandlerOptions } from 'custom-card-helpers/dist/types'; | ||
import { fireEvent } from 'custom-card-helpers'; | ||
import { type ActionHandlerDetail, type ActionHandlerOptions } from 'custom-card-helpers/dist/types' | ||
import { fireEvent } from 'custom-card-helpers' | ||
|
||
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0; | ||
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 | ||
|
||
interface ActionHandler extends HTMLElement { | ||
holdTime: number; | ||
bind(element: Element, options): void; | ||
holdTime: number | ||
bind: (element: Element, options) => void | ||
} | ||
interface ActionHandlerElement extends HTMLElement { | ||
actionHandler?: boolean; | ||
actionHandler?: boolean | ||
} | ||
|
||
declare global { | ||
interface HASSDomEvents { | ||
action: ActionHandlerDetail; | ||
action: ActionHandlerDetail | ||
} | ||
} | ||
|
||
class ActionHandler extends HTMLElement implements ActionHandler { | ||
public holdTime = 500; | ||
public holdTime = 500 | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
public ripple: any; | ||
public ripple: any | ||
|
||
protected timer?: number; | ||
protected timer?: number | ||
|
||
protected held = false; | ||
protected held = false | ||
|
||
private dblClickTimeout?: number; | ||
private dblClickTimeout?: number | ||
|
||
constructor() { | ||
super(); | ||
this.ripple = document.createElement('mwc-ripple'); | ||
constructor () { | ||
super() | ||
this.ripple = document.createElement('mwc-ripple') | ||
} | ||
|
||
public connectedCallback(): void { | ||
public connectedCallback (): void { | ||
Object.assign(this.style, { | ||
position: 'absolute', | ||
width: isTouch ? '100px' : '50px', | ||
height: isTouch ? '100px' : '50px', | ||
transform: 'translate(-50%, -50%)', | ||
pointerEvents: 'none', | ||
zIndex: '999', | ||
}); | ||
zIndex: '999' | ||
}) | ||
|
||
this.appendChild(this.ripple); | ||
this.appendChild(this.ripple) | ||
this.ripple.primary = true; | ||
|
||
['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach((ev) => { | ||
document.addEventListener( | ||
ev, | ||
() => { | ||
clearTimeout(this.timer); | ||
this.stopAnimation(); | ||
this.timer = undefined; | ||
clearTimeout(this.timer) | ||
this.stopAnimation() | ||
this.timer = undefined | ||
}, | ||
{ passive: true }, | ||
); | ||
}); | ||
{ passive: true } | ||
) | ||
}) | ||
} | ||
|
||
public bind(element: ActionHandlerElement, options): void { | ||
public bind (element: ActionHandlerElement, options): void { | ||
if (element.actionHandler) { | ||
return; | ||
return | ||
} | ||
element.actionHandler = true; | ||
element.actionHandler = true | ||
|
||
element.addEventListener('contextmenu', (ev: Event) => { | ||
const e = ev || window.event; | ||
const e = ev || window.event | ||
if (e.preventDefault) { | ||
e.preventDefault(); | ||
e.preventDefault() | ||
} | ||
if (e.stopPropagation) { | ||
e.stopPropagation(); | ||
e.stopPropagation() | ||
} | ||
e.cancelBubble = true; | ||
e.returnValue = false; | ||
return false; | ||
}); | ||
e.cancelBubble = true | ||
e.returnValue = false | ||
return false | ||
}) | ||
|
||
const start = (ev: Event): void => { | ||
this.held = false; | ||
let x; | ||
let y; | ||
this.held = false | ||
let x | ||
let y | ||
if ((ev as TouchEvent).touches) { | ||
x = (ev as TouchEvent).touches[0].pageX; | ||
y = (ev as TouchEvent).touches[0].pageY; | ||
x = (ev as TouchEvent).touches[0].pageX | ||
y = (ev as TouchEvent).touches[0].pageY | ||
} else { | ||
x = (ev as MouseEvent).pageX; | ||
y = (ev as MouseEvent).pageY; | ||
x = (ev as MouseEvent).pageX | ||
y = (ev as MouseEvent).pageY | ||
} | ||
|
||
this.timer = window.setTimeout(() => { | ||
this.startAnimation(x, y); | ||
this.held = true; | ||
}, this.holdTime); | ||
}; | ||
this.startAnimation(x, y) | ||
this.held = true | ||
}, this.holdTime) | ||
} | ||
|
||
const end = (ev: Event): void => { | ||
// Prevent mouse event if touch event | ||
ev.preventDefault(); | ||
ev.preventDefault() | ||
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) { | ||
return; | ||
return | ||
} | ||
clearTimeout(this.timer); | ||
this.stopAnimation(); | ||
this.timer = undefined; | ||
clearTimeout(this.timer) | ||
this.stopAnimation() | ||
this.timer = undefined | ||
if (this.held) { | ||
fireEvent(element, 'action', { action: 'hold' }); | ||
fireEvent(element, 'action', { action: 'hold' }) | ||
} else if (options.hasDoubleClick) { | ||
if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) { | ||
this.dblClickTimeout = window.setTimeout(() => { | ||
this.dblClickTimeout = undefined; | ||
fireEvent(element, 'action', { action: 'tap' }); | ||
}, 250); | ||
this.dblClickTimeout = undefined | ||
fireEvent(element, 'action', { action: 'tap' }) | ||
}, 250) | ||
} else { | ||
clearTimeout(this.dblClickTimeout); | ||
this.dblClickTimeout = undefined; | ||
fireEvent(element, 'action', { action: 'double_tap' }); | ||
clearTimeout(this.dblClickTimeout) | ||
this.dblClickTimeout = undefined | ||
fireEvent(element, 'action', { action: 'double_tap' }) | ||
} | ||
} else { | ||
fireEvent(element, 'action', { action: 'tap' }); | ||
fireEvent(element, 'action', { action: 'tap' }) | ||
} | ||
}; | ||
} | ||
|
||
const handleEnter = (ev: KeyboardEvent): void => { | ||
if (ev.keyCode !== 13) { | ||
return; | ||
return | ||
} | ||
end(ev); | ||
}; | ||
end(ev) | ||
} | ||
|
||
element.addEventListener('touchstart', start, { passive: true }); | ||
element.addEventListener('touchend', end); | ||
element.addEventListener('touchcancel', end); | ||
element.addEventListener('touchstart', start, { passive: true }) | ||
element.addEventListener('touchend', end) | ||
element.addEventListener('touchcancel', end) | ||
|
||
element.addEventListener('mousedown', start, { passive: true }); | ||
element.addEventListener('click', end); | ||
element.addEventListener('mousedown', start, { passive: true }) | ||
element.addEventListener('click', end) | ||
|
||
element.addEventListener('keyup', handleEnter); | ||
element.addEventListener('keyup', handleEnter) | ||
} | ||
|
||
private startAnimation(x: number, y: number): void { | ||
private startAnimation (x: number, y: number): void { | ||
Object.assign(this.style, { | ||
left: `${x}px`, | ||
top: `${y}px`, | ||
display: null, | ||
}); | ||
this.ripple.disabled = false; | ||
this.ripple.active = true; | ||
this.ripple.unbounded = true; | ||
display: null | ||
}) | ||
this.ripple.disabled = false | ||
this.ripple.active = true | ||
this.ripple.unbounded = true | ||
} | ||
|
||
private stopAnimation(): void { | ||
this.ripple.active = false; | ||
this.ripple.disabled = true; | ||
this.style.display = 'none'; | ||
private stopAnimation (): void { | ||
this.ripple.active = false | ||
this.ripple.disabled = true | ||
this.style.display = 'none' | ||
} | ||
} | ||
|
||
customElements.define('action-handler-clock-weather', ActionHandler); | ||
customElements.define('action-handler-clock-weather', ActionHandler) | ||
|
||
const getActionHandler = (): ActionHandler => { | ||
const body = document.body; | ||
const body = document.body | ||
if (body.querySelector('action-handler-clock-weather')) { | ||
return body.querySelector('action-handler-clock-weather') as ActionHandler; | ||
return body.querySelector('action-handler-clock-weather') as ActionHandler | ||
} | ||
|
||
const actionhandler = document.createElement('action-handler-clock-weather'); | ||
body.appendChild(actionhandler); | ||
const actionhandler = document.createElement('action-handler-clock-weather') | ||
body.appendChild(actionhandler) | ||
|
||
return actionhandler as ActionHandler; | ||
}; | ||
return actionhandler as ActionHandler | ||
} | ||
|
||
export const actionHandlerBind = (element: ActionHandlerElement, options?: ActionHandlerOptions): void => { | ||
const actionhandler: ActionHandler = getActionHandler(); | ||
const actionhandler: ActionHandler = getActionHandler() | ||
if (!actionhandler) { | ||
return; | ||
return | ||
} | ||
actionhandler.bind(element, options); | ||
}; | ||
actionhandler.bind(element, options) | ||
} | ||
|
||
export const actionHandler = directive( | ||
class extends Directive { | ||
update(part: AttributePart, [options]: DirectiveParameters<this>) { | ||
actionHandlerBind(part.element as ActionHandlerElement, options); | ||
return noChange; | ||
update (part: AttributePart, [options]: DirectiveParameters<this>): undefined { | ||
actionHandlerBind(part.element as ActionHandlerElement, options) | ||
return noChange | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars | ||
render(_options?: ActionHandlerOptions) {} | ||
}, | ||
); | ||
render (_options?: ActionHandlerOptions): void {} | ||
} | ||
) |
Oops, something went wrong.