custom-card-helpers
This commit is contained in:
@@ -12,6 +12,10 @@ rules:
|
|||||||
no-nested-ternary: 0
|
no-nested-ternary: 0
|
||||||
camelcase: 0
|
camelcase: 0
|
||||||
no-unused-vars: 0
|
no-unused-vars: 0
|
||||||
|
quotes: 0
|
||||||
|
comma-dangle: 0
|
||||||
|
import/no-unresolved: 0
|
||||||
|
import/prefer-default-export: 0
|
||||||
globals:
|
globals:
|
||||||
window: true
|
window: true
|
||||||
Event: true
|
Event: true
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
/.rpt2_cache/
|
/.rpt2_cache/
|
||||||
yarn.lock
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Boilerplate Card
|
# Boilerplate Card
|
||||||
A community driven boilerplate of best practices from Home Assistant Lovelace custom cards
|
|
||||||
|
A community driven boilerplate of best practices for Home Assistant Lovelace custom cards
|
||||||
|
|
||||||
[![GitHub Release][releases-shield]][releases]
|
[![GitHub Release][releases-shield]][releases]
|
||||||
[![License][license-shield]](LICENSE.md)
|
[![License][license-shield]](LICENSE.md)
|
||||||
@@ -12,27 +13,45 @@ A community driven boilerplate of best practices from Home Assistant Lovelace cu
|
|||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
| Name | Type | Requirement | Description
|
| Name | Type | Requirement | Description | Default
|
||||||
| ---- | ---- | ------- | -----------
|
| ---- | ---- | ------- | ----------- | -------
|
||||||
| type | string | **Required** | `custom:boilerplate-card`
|
| type | string | **Required** | `custom:boilerplate-card`
|
||||||
| name | string | **Optional** | Card name
|
| name | string | **Optional** | Card name | `Boilerplate`
|
||||||
| show_error | boolean | **Optional** | Show what an error looks like for the card
|
| show_error | boolean | **Optional** | Show what an error looks like for the card | `false`
|
||||||
| show_warning | boolean | **Optional** | Show what a warning looks like for the card
|
| show_warning | boolean | **Optional** | Show what a warning looks like for the card | `false`
|
||||||
|
| entity | string | **Optional** | Home Assistant entity ID. | `none`
|
||||||
|
| tap_action | object | **Optional** | Action to take on tap | `action: more-info`
|
||||||
|
| hold_action | object | **Optional** | Action to take on hold | `none`
|
||||||
|
|
||||||
|
## Action Options
|
||||||
|
|
||||||
|
| Name | Type | Requirement | Description | Default
|
||||||
|
| ---- | ---- | ------- | ----------- | -------
|
||||||
|
| action | string | **Required** | Action to perform (toggle-menu, more-info, toggle, call-service, navigate url, none) | `toggle-menu` for menu and `more-info` for items
|
||||||
|
| navigation_path | string | **Optional** | Path to navigate to (e.g. /lovelace/0/) when action defined as navigate | `none`
|
||||||
|
| url | string | **Optional** | URL to open on click when action is url. The URL will open in a new tab | `none`
|
||||||
|
| service | string | **Optional** | Service to call (e.g. media_player.media_play_pause) when action defined as call-service | `none`
|
||||||
|
| service_data | object | **Optional** | Service data to include (e.g. entity_id: media_player.bedroom) when action defined as call-service | `none`
|
||||||
|
| haptic | string | **Optional** | Haptic feedback for the [Beta IOS App](http://home-assistant.io/ios/beta) _success, warning, failure, light, medium, heavy, selection_ | `none`
|
||||||
|
|
||||||
## Starting a new card from boilerplate-card
|
## Starting a new card from boilerplate-card
|
||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
|
||||||
Clone this repo
|
Clone this repo
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
|
||||||
Install necessary modules
|
Install necessary modules
|
||||||
`yarn install` or `npm install`
|
`yarn install` or `npm install`
|
||||||
|
|
||||||
### Step 3
|
### Step 3
|
||||||
|
|
||||||
Do a test lint & build on the project. You can see available scripts in the package.json
|
Do a test lint & build on the project. You can see available scripts in the package.json
|
||||||
`npm run build`
|
`npm run build`
|
||||||
|
|
||||||
### Step 4
|
### Step 4
|
||||||
|
|
||||||
Customize to suit your needs and contribute it back to the custom-cards org
|
Customize to suit your needs and contribute it back to the custom-cards org
|
||||||
|
|
||||||
[Troubleshooting](https://github.com/thomasloven/hass-config/wiki/Lovelace-Plugins)
|
[Troubleshooting](https://github.com/thomasloven/hass-config/wiki/Lovelace-Plugins)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
Vendored
+131
-56
@@ -1543,7 +1543,6 @@ class UpdatingElement extends HTMLElement {
|
|||||||
Object.defineProperty(this.prototype, name, {
|
Object.defineProperty(this.prototype, name, {
|
||||||
// tslint:disable-next-line:no-any no symbol in index
|
// tslint:disable-next-line:no-any no symbol in index
|
||||||
get() {
|
get() {
|
||||||
// tslint:disable-next-line:no-any no symbol in index
|
|
||||||
return this[key];
|
return this[key];
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -1551,7 +1550,7 @@ class UpdatingElement extends HTMLElement {
|
|||||||
const oldValue = this[name];
|
const oldValue = this[name];
|
||||||
// tslint:disable-next-line:no-any no symbol in index
|
// tslint:disable-next-line:no-any no symbol in index
|
||||||
this[key] = value;
|
this[key] = value;
|
||||||
this.requestUpdate(name, oldValue);
|
this._requestUpdate(name, oldValue);
|
||||||
},
|
},
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true
|
enumerable: true
|
||||||
@@ -1655,6 +1654,8 @@ class UpdatingElement extends HTMLElement {
|
|||||||
*/
|
*/
|
||||||
initialize() {
|
initialize() {
|
||||||
this._saveInstanceProperties();
|
this._saveInstanceProperties();
|
||||||
|
// ensures first update will be caught by an early access of `updateComplete`
|
||||||
|
this._requestUpdate();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Fixes any properties set on the instance before upgrade time.
|
* Fixes any properties set on the instance before upgrade time.
|
||||||
@@ -1695,7 +1696,7 @@ class UpdatingElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this._updateState = this._updateState | STATE_HAS_CONNECTED;
|
this._updateState = this._updateState | STATE_HAS_CONNECTED;
|
||||||
// Ensure connection triggers an update. Updates cannot complete before
|
// Ensure first connection completes an update. Updates cannot complete before
|
||||||
// connection and if one is pending connection the `_hasConnectionResolver`
|
// connection and if one is pending connection the `_hasConnectionResolver`
|
||||||
// will exist. If so, resolve it to complete the update, otherwise
|
// will exist. If so, resolve it to complete the update, otherwise
|
||||||
// requestUpdate.
|
// requestUpdate.
|
||||||
@@ -1703,9 +1704,6 @@ class UpdatingElement extends HTMLElement {
|
|||||||
this._hasConnectedResolver();
|
this._hasConnectedResolver();
|
||||||
this._hasConnectedResolver = undefined;
|
this._hasConnectedResolver = undefined;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Allows for `super.disconnectedCallback()` in extensions while
|
* Allows for `super.disconnectedCallback()` in extensions while
|
||||||
@@ -1769,6 +1767,42 @@ class UpdatingElement extends HTMLElement {
|
|||||||
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
|
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This private version of `requestUpdate` does not access or return the
|
||||||
|
* `updateComplete` promise. This promise can be overridden and is therefore
|
||||||
|
* not free to access.
|
||||||
|
*/
|
||||||
|
_requestUpdate(name, oldValue) {
|
||||||
|
let shouldRequestUpdate = true;
|
||||||
|
// If we have a property key, perform property update steps.
|
||||||
|
if (name !== undefined) {
|
||||||
|
const ctor = this.constructor;
|
||||||
|
const options = ctor._classProperties.get(name) || defaultPropertyDeclaration;
|
||||||
|
if (ctor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
|
||||||
|
if (!this._changedProperties.has(name)) {
|
||||||
|
this._changedProperties.set(name, oldValue);
|
||||||
|
}
|
||||||
|
// Add to reflecting properties set.
|
||||||
|
// Note, it's important that every change has a chance to add the
|
||||||
|
// property to `_reflectingProperties`. This ensures setting
|
||||||
|
// attribute + property reflects correctly.
|
||||||
|
if (options.reflect === true &&
|
||||||
|
!(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) {
|
||||||
|
if (this._reflectingProperties === undefined) {
|
||||||
|
this._reflectingProperties = new Map();
|
||||||
|
}
|
||||||
|
this._reflectingProperties.set(name, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Abort the request if the property should not be considered changed.
|
||||||
|
shouldRequestUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this._hasRequestedUpdate && shouldRequestUpdate) {
|
||||||
|
this._enqueueUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Requests an update which is processed asynchronously. This should
|
* Requests an update which is processed asynchronously. This should
|
||||||
* be called when an element should update based on some state not triggered
|
* be called when an element should update based on some state not triggered
|
||||||
@@ -1783,31 +1817,7 @@ class UpdatingElement extends HTMLElement {
|
|||||||
* @returns {Promise} A Promise that is resolved when the update completes.
|
* @returns {Promise} A Promise that is resolved when the update completes.
|
||||||
*/
|
*/
|
||||||
requestUpdate(name, oldValue) {
|
requestUpdate(name, oldValue) {
|
||||||
let shouldRequestUpdate = true;
|
this._requestUpdate(name, oldValue);
|
||||||
// if we have a property key, perform property update steps.
|
|
||||||
if (name !== undefined && !this._changedProperties.has(name)) {
|
|
||||||
const ctor = this.constructor;
|
|
||||||
const options = ctor._classProperties.get(name) || defaultPropertyDeclaration;
|
|
||||||
if (ctor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
|
|
||||||
// track old value when changing.
|
|
||||||
this._changedProperties.set(name, oldValue);
|
|
||||||
// add to reflecting properties set
|
|
||||||
if (options.reflect === true &&
|
|
||||||
!(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) {
|
|
||||||
if (this._reflectingProperties === undefined) {
|
|
||||||
this._reflectingProperties = new Map();
|
|
||||||
}
|
|
||||||
this._reflectingProperties.set(name, options);
|
|
||||||
}
|
|
||||||
// abort the request if the property should not be considered changed.
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
shouldRequestUpdate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._hasRequestedUpdate && shouldRequestUpdate) {
|
|
||||||
this._enqueueUpdate();
|
|
||||||
}
|
|
||||||
return this.updateComplete;
|
return this.updateComplete;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1817,22 +1827,36 @@ class UpdatingElement extends HTMLElement {
|
|||||||
// Mark state updating...
|
// Mark state updating...
|
||||||
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
|
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
|
||||||
let resolve;
|
let resolve;
|
||||||
|
let reject;
|
||||||
const previousUpdatePromise = this._updatePromise;
|
const previousUpdatePromise = this._updatePromise;
|
||||||
this._updatePromise = new Promise((res) => resolve = res);
|
this._updatePromise = new Promise((res, rej) => {
|
||||||
// Ensure any previous update has resolved before updating.
|
resolve = res;
|
||||||
// This `await` also ensures that property changes are batched.
|
reject = rej;
|
||||||
await previousUpdatePromise;
|
});
|
||||||
|
try {
|
||||||
|
// Ensure any previous update has resolved before updating.
|
||||||
|
// This `await` also ensures that property changes are batched.
|
||||||
|
await previousUpdatePromise;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// Ignore any previous errors. We only care that the previous cycle is
|
||||||
|
// done. Any error should have been handled in the previous update.
|
||||||
|
}
|
||||||
// Make sure the element has connected before updating.
|
// Make sure the element has connected before updating.
|
||||||
if (!this._hasConnected) {
|
if (!this._hasConnected) {
|
||||||
await new Promise((res) => this._hasConnectedResolver = res);
|
await new Promise((res) => this._hasConnectedResolver = res);
|
||||||
}
|
}
|
||||||
// Allow `performUpdate` to be asynchronous to enable scheduling of updates.
|
try {
|
||||||
const result = this.performUpdate();
|
const result = this.performUpdate();
|
||||||
// Note, this is to avoid delaying an additional microtask unless we need
|
// If `performUpdate` returns a Promise, we await it. This is done to
|
||||||
// to.
|
// enable coordinating updates with a scheduler. Note, the result is
|
||||||
if (result != null &&
|
// checked to avoid delaying an additional microtask unless we need to.
|
||||||
typeof result.then === 'function') {
|
if (result != null) {
|
||||||
await result;
|
await result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
reject(e);
|
||||||
}
|
}
|
||||||
resolve(!this._hasRequestedUpdate);
|
resolve(!this._hasRequestedUpdate);
|
||||||
}
|
}
|
||||||
@@ -1846,10 +1870,13 @@ class UpdatingElement extends HTMLElement {
|
|||||||
return (this._updateState & STATE_HAS_UPDATED);
|
return (this._updateState & STATE_HAS_UPDATED);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Performs an element update.
|
* Performs an element update. Note, if an exception is thrown during the
|
||||||
|
* update, `firstUpdated` and `updated` will not be called.
|
||||||
*
|
*
|
||||||
* You can override this method to change the timing of updates. For instance,
|
* You can override this method to change the timing of updates. If this
|
||||||
* to schedule updates to occur just before the next frame:
|
* method is overridden, `super.performUpdate()` must be called.
|
||||||
|
*
|
||||||
|
* For instance, to schedule updates to occur just before the next frame:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* protected async performUpdate(): Promise<unknown> {
|
* protected async performUpdate(): Promise<unknown> {
|
||||||
@@ -1863,19 +1890,31 @@ class UpdatingElement extends HTMLElement {
|
|||||||
if (this._instanceProperties) {
|
if (this._instanceProperties) {
|
||||||
this._applyInstanceProperties();
|
this._applyInstanceProperties();
|
||||||
}
|
}
|
||||||
if (this.shouldUpdate(this._changedProperties)) {
|
let shouldUpdate = false;
|
||||||
const changedProperties = this._changedProperties;
|
const changedProperties = this._changedProperties;
|
||||||
this.update(changedProperties);
|
try {
|
||||||
|
shouldUpdate = this.shouldUpdate(changedProperties);
|
||||||
|
if (shouldUpdate) {
|
||||||
|
this.update(changedProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// Prevent `firstUpdated` and `updated` from running when there's an
|
||||||
|
// update exception.
|
||||||
|
shouldUpdate = false;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Ensure element can accept additional updates after an exception.
|
||||||
this._markUpdated();
|
this._markUpdated();
|
||||||
|
}
|
||||||
|
if (shouldUpdate) {
|
||||||
if (!(this._updateState & STATE_HAS_UPDATED)) {
|
if (!(this._updateState & STATE_HAS_UPDATED)) {
|
||||||
this._updateState = this._updateState | STATE_HAS_UPDATED;
|
this._updateState = this._updateState | STATE_HAS_UPDATED;
|
||||||
this.firstUpdated(changedProperties);
|
this.firstUpdated(changedProperties);
|
||||||
}
|
}
|
||||||
this.updated(changedProperties);
|
this.updated(changedProperties);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this._markUpdated();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_markUpdated() {
|
_markUpdated() {
|
||||||
this._changedProperties = new Map();
|
this._changedProperties = new Map();
|
||||||
@@ -1885,7 +1924,8 @@ class UpdatingElement extends HTMLElement {
|
|||||||
* Returns a Promise that resolves when the element has completed updating.
|
* Returns a Promise that resolves when the element has completed updating.
|
||||||
* The Promise value is a boolean that is `true` if the element completed the
|
* The Promise value is a boolean that is `true` if the element completed the
|
||||||
* update without triggering another update. The Promise result is `false` if
|
* update without triggering another update. The Promise result is `false` if
|
||||||
* a property was set inside `updated()`. This getter can be implemented to
|
* a property was set inside `updated()`. If the Promise is rejected, an
|
||||||
|
* exception was thrown during the update. This getter can be implemented to
|
||||||
* await additional state. For example, it is sometimes useful to await a
|
* await additional state. For example, it is sometimes useful to await a
|
||||||
* rendered element before fulfilling this Promise. To do this, first await
|
* rendered element before fulfilling this Promise. To do this, first await
|
||||||
* `super.updateComplete` then any subsequent state.
|
* `super.updateComplete` then any subsequent state.
|
||||||
@@ -2196,7 +2236,8 @@ class LitElement extends UpdatingElement {
|
|||||||
*/
|
*/
|
||||||
initialize() {
|
initialize() {
|
||||||
super.initialize();
|
super.initialize();
|
||||||
this.renderRoot = this.createRenderRoot();
|
this.renderRoot =
|
||||||
|
this.createRenderRoot();
|
||||||
// Note, if renderRoot is not a shadowRoot, styles would/could apply to the
|
// Note, if renderRoot is not a shadowRoot, styles would/could apply to the
|
||||||
// element's getRootNode(). While this could be done, we're choosing not to
|
// element's getRootNode(). While this could be done, we're choosing not to
|
||||||
// support this now since it would require different logic around de-duping.
|
// support this now since it would require different logic around de-duping.
|
||||||
@@ -2302,15 +2343,38 @@ LitElement.finalized = true;
|
|||||||
*/
|
*/
|
||||||
LitElement.render = render$1;
|
LitElement.render = render$1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse or format dates
|
||||||
|
* @class fecha
|
||||||
|
*/
|
||||||
|
|
||||||
|
function shorten(arr, sLen) {
|
||||||
|
var newArr = [];
|
||||||
|
for (var i = 0, len = arr.length; i < len; i++) {
|
||||||
|
newArr.push(arr[i].substr(0, sLen));
|
||||||
|
}
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||||
|
var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||||
|
var monthNamesShort = shorten(monthNames, 3);
|
||||||
|
var dayNamesShort = shorten(dayNames, 3);
|
||||||
|
|
||||||
|
function e(t){return t.substr(0,t.indexOf("."))}!function(){try{(new Date).toLocaleDateString("i");}catch(t){return "RangeError"===t.name}}(),function(){try{(new Date).toLocaleString("i");}catch(t){return "RangeError"===t.name}}(),function(){try{(new Date).toLocaleTimeString("i");}catch(t){return "RangeError"===t.name}}();var s=["closed","locked","off"],h=function(t,e,n,o){o=o||{},n=null==n?{}:n;var i=new Event(e,{bubbles:void 0===o.bubbles||o.bubbles,cancelable:Boolean(o.cancelable),composed:void 0===o.composed||o.composed});return i.detail=n,t.dispatchEvent(i),i},m=function(t,e,n){void 0===n&&(n=!1),n?history.replaceState(null,"",e):history.pushState(null,"",e),h(window,"location-changed",{replace:n});},v=function(t,n,o){void 0===o&&(o=!0);var i,r=e(n),a="group"===r?"homeassistant":r;switch(r){case"lock":i=o?"unlock":"lock";break;case"cover":i=o?"open_cover":"close_cover";break;default:i=o?"turn_on":"turn_off";}return t.callService(a,i,{entity_id:n})},f=function(t,e){var n=s.includes(t.states[e].state);return v(t,e,n)},w=function(t,e){h(t,"haptic",e);},g=function(t,e,n,o){var i;switch(o&&n.hold_action?i=n.hold_action:!o&&n.tap_action&&(i=n.tap_action),i||(i={action:"more-info"}),i.action){case"more-info":n.entity&&(h(t,"hass-more-info",{entityId:n.entity}),i.haptic&&w(t,i.haptic));break;case"navigate":i.navigation_path&&(m(0,i.navigation_path),i.haptic&&w(t,i.haptic));break;case"url":i.url&&window.open(i.url),i.haptic&&w(t,i.haptic);break;case"toggle":n.entity&&(f(e,n.entity),i.haptic&&w(t,i.haptic));break;case"call-service":if(!i.service)return;var r=i.service.split(".",2);e.callService(r[0],r[1],i.service_data),i.haptic&&w(t,i.haptic);}};function _(t,e){if(e.has("_config"))return !0;var n=e.get("hass");return !n||n.states[t._config.entity]!==t.hass.states[t._config.entity]}const b=new WeakMap;String(Math.random()).slice(2);try{const t={get capture(){return !1}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t);}catch(t){}(window.litHtmlVersions||(window.litHtmlVersions=[])).push("1.0.0");var y="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,E=function(t){function e(){t.call(this),this.holdTime=500,this.ripple=document.createElement("paper-ripple"),this.timer=void 0,this.held=!1,this.cooldownStart=!1,this.cooldownEnd=!1;}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.connectedCallback=function(){var t=this;Object.assign(this.style,{borderRadius:"50%",position:"absolute",width:y?"100px":"50px",height:y?"100px":"50px",transform:"translate(-50%, -50%)",pointerEvents:"none"}),this.appendChild(this.ripple),this.ripple.style.color="#03a9f4",this.ripple.style.color="var(--primary-color)",["touchcancel","mouseout","mouseup","touchmove","mousewheel","wheel","scroll"].forEach(function(e){document.addEventListener(e,function(){clearTimeout(t.timer),t.stopAnimation(),t.timer=void 0;},{passive:!0});});},e.prototype.bind=function(t){var e=this;if(!t.longPress){t.longPress=!0,t.addEventListener("contextmenu",function(t){var e=t||window.event;return e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0,e.returnValue=!1,!1});var n=function(t){var n,o;e.cooldownStart||(e.held=!1,t.touches?(n=t.touches[0].pageX,o=t.touches[0].pageY):(n=t.pageX,o=t.pageY),e.timer=window.setTimeout(function(){e.startAnimation(n,o),e.held=!0;},e.holdTime),e.cooldownStart=!0,window.setTimeout(function(){return e.cooldownStart=!1},100));},o=function(n){e.cooldownEnd||["touchend","touchcancel"].includes(n.type)&&void 0===e.timer||(clearTimeout(e.timer),e.stopAnimation(),e.timer=void 0,t.dispatchEvent(e.held?new Event("ha-hold"):new Event("ha-click")),e.cooldownEnd=!0,window.setTimeout(function(){return e.cooldownEnd=!1},100));};t.addEventListener("touchstart",n,{passive:!0}),t.addEventListener("touchend",o),t.addEventListener("touchcancel",o),t.addEventListener("mousedown",n,{passive:!0}),t.addEventListener("click",o);}},e.prototype.startAnimation=function(t,e){Object.assign(this.style,{left:t+"px",top:e+"px",display:null}),this.ripple.holdDown=!0,this.ripple.simulatedRipple();},e.prototype.stopAnimation=function(){this.ripple.holdDown=!1,this.style.display="none";},e}(HTMLElement);customElements.get("long-press")||customElements.define("long-press",E);var k=function(t){var e=function(){var t=document.body;if(t.querySelector("long-press"))return t.querySelector("long-press");var e=document.createElement("long-press");return t.appendChild(e),e}();e&&e.bind(t);},S=(t=>(...t)=>{const e=function(){return function(t){k(t.committer.element);}}(...t);return b.set(e,!0),e})();
|
||||||
|
|
||||||
// TODO Name your custom element
|
// TODO Name your custom element
|
||||||
let BoilerplateCard = class BoilerplateCard extends LitElement {
|
let BoilerplateCard = class BoilerplateCard extends LitElement {
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
// TODO Check for required fields and that they are of the proper format
|
// TODO Check for required fields and that they are of the proper format
|
||||||
if (!config || config.show_error) {
|
if (!config || config.show_error) {
|
||||||
throw new Error('Invalid configuration');
|
throw new Error("Invalid configuration");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
shouldUpdate(changedProps) {
|
||||||
|
return _(this, changedProps);
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return html ``;
|
return html ``;
|
||||||
@@ -2324,9 +2388,20 @@ let BoilerplateCard = class BoilerplateCard extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return html `
|
return html `
|
||||||
<ha-card .header=${this._config.name ? this._config.name : 'Boilerplate'}></ha-card>
|
<ha-card
|
||||||
|
.header=${this._config.name ? this._config.name : "Boilerplate"}
|
||||||
|
@ha-click="${this._handleTap}"
|
||||||
|
@ha-hold="${this._handleHold}"
|
||||||
|
.longpress="${S()}"
|
||||||
|
></ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
_handleTap() {
|
||||||
|
g(this, this.hass, this._config, false);
|
||||||
|
}
|
||||||
|
_handleHold() {
|
||||||
|
g(this, this.hass, this._config, true);
|
||||||
|
}
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css `
|
return css `
|
||||||
.warning {
|
.warning {
|
||||||
@@ -2345,5 +2420,5 @@ __decorate([
|
|||||||
property()
|
property()
|
||||||
], BoilerplateCard.prototype, "_config", void 0);
|
], BoilerplateCard.prototype, "_config", void 0);
|
||||||
BoilerplateCard = __decorate([
|
BoilerplateCard = __decorate([
|
||||||
customElement('boilerplate-card')
|
customElement("boilerplate-card")
|
||||||
], BoilerplateCard);
|
], BoilerplateCard);
|
||||||
+9
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "boilerplate-card",
|
"name": "boilerplate-card",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "Lovelace boilerplate-card",
|
"description": "Lovelace boilerplate-card",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"home-assistant",
|
"home-assistant",
|
||||||
@@ -15,16 +15,22 @@
|
|||||||
"author": "BoilerPlate <boilerplate@email.com>",
|
"author": "BoilerPlate <boilerplate@email.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"custom-card-helpers": "^1.0.8",
|
||||||
|
"home-assistant-js-websocket": "^3.4.0",
|
||||||
"lit-element": "^2.0.1"
|
"lit-element": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.4.3",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.4.0",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.4.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^1.4.2",
|
||||||
"@typescript-eslint/parser": "^1.4.1",
|
"@typescript-eslint/parser": "^1.4.1",
|
||||||
"eslint": "^5.14.1",
|
"eslint": "^5.14.1",
|
||||||
"eslint-config-airbnb-base": "^13.1.0",
|
"eslint-config-airbnb-base": "^13.1.0",
|
||||||
"eslint-plugin-import": "^2.16.0",
|
"eslint-plugin-import": "^2.16.0",
|
||||||
"eslint-plugin-typescript": "^0.14.0",
|
|
||||||
"prettier": "^1.16.4",
|
"prettier": "^1.16.4",
|
||||||
"rollup": "^1.2.3",
|
"rollup": "^1.2.3",
|
||||||
|
"rollup-plugin-babel": "^4.3.2",
|
||||||
"rollup-plugin-node-resolve": "^4.0.1",
|
"rollup-plugin-node-resolve": "^4.0.1",
|
||||||
"rollup-plugin-typescript2": "^0.19.2",
|
"rollup-plugin-typescript2": "^0.19.2",
|
||||||
"typescript": "^3.3.3333"
|
"typescript": "^3.3.3333"
|
||||||
@@ -32,7 +38,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "rollup -c --watch",
|
"start": "rollup -c --watch",
|
||||||
"build": "npm run lint && npm run rollup",
|
"build": "npm run lint && npm run rollup",
|
||||||
"lint": "eslint src/index.ts",
|
"lint": "eslint src/*.ts",
|
||||||
"rollup": "rollup -c"
|
"rollup": "rollup -c"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-24
@@ -1,27 +1,17 @@
|
|||||||
import resolve from 'rollup-plugin-node-resolve';
|
import resolve from 'rollup-plugin-node-resolve';
|
||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
|
import babel from 'rollup-plugin-babel';
|
||||||
|
|
||||||
|
export default {
|
||||||
const commonPlugins = [
|
input: ['src/card.ts'],
|
||||||
|
output: {
|
||||||
|
dir: './dist',
|
||||||
|
format: 'es',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
resolve(),
|
resolve(),
|
||||||
typescript()
|
typescript(),
|
||||||
];
|
babel({
|
||||||
|
exclude: 'node_modules/**'
|
||||||
export default [
|
})],
|
||||||
{
|
};
|
||||||
input: 'src/index.ts',
|
|
||||||
output: {
|
|
||||||
file: 'boilerplate-card.js',
|
|
||||||
format: 'es'
|
|
||||||
},
|
|
||||||
plugins: [...commonPlugins]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'src/editor.ts',
|
|
||||||
output: {
|
|
||||||
file: 'boilerplate-card-editor.js',
|
|
||||||
format: 'es'
|
|
||||||
},
|
|
||||||
plugins: [...commonPlugins]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|||||||
+30
-12
@@ -6,33 +6,38 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
css,
|
css,
|
||||||
} from 'lit-element';
|
PropertyValues
|
||||||
|
} from "lit-element";
|
||||||
|
import {
|
||||||
|
HomeAssistant,
|
||||||
|
handleClick,
|
||||||
|
longPress,
|
||||||
|
hasConfigOrEntityChanged
|
||||||
|
} from "custom-card-helpers";
|
||||||
|
|
||||||
// TODO Add your configuration elements here for type-checking
|
import { BoilerplateConfig } from "./types";
|
||||||
interface BoilerplateConfig {
|
|
||||||
type: string;
|
|
||||||
name?: string;
|
|
||||||
show_warning?: boolean;
|
|
||||||
show_error?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Name your custom element
|
// TODO Name your custom element
|
||||||
@customElement('boilerplate-card')
|
@customElement("boilerplate-card")
|
||||||
class BoilerplateCard extends LitElement {
|
class BoilerplateCard extends LitElement {
|
||||||
// TODO Add any properities that should cause your element to re-render here
|
// TODO Add any properities that should cause your element to re-render here
|
||||||
@property() public hass?: any;
|
@property() public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() private _config?: BoilerplateConfig;
|
@property() private _config?: BoilerplateConfig;
|
||||||
|
|
||||||
public setConfig(config: BoilerplateConfig): void {
|
public setConfig(config: BoilerplateConfig): void {
|
||||||
// TODO Check for required fields and that they are of the proper format
|
// TODO Check for required fields and that they are of the proper format
|
||||||
if (!config || config.show_error) {
|
if (!config || config.show_error) {
|
||||||
throw new Error('Invalid configuration');
|
throw new Error("Invalid configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -48,10 +53,23 @@ class BoilerplateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card .header=${this._config.name ? this._config.name : 'Boilerplate'}></ha-card>
|
<ha-card
|
||||||
|
.header=${this._config.name ? this._config.name : "Boilerplate"}
|
||||||
|
@ha-click="${this._handleTap}"
|
||||||
|
@ha-hold="${this._handleHold}"
|
||||||
|
.longpress="${longPress()}"
|
||||||
|
></ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleTap(): void {
|
||||||
|
handleClick(this, this.hass!, this._config!, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleHold(): void {
|
||||||
|
handleClick(this, this.hass!, this._config!, true);
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
.warning {
|
.warning {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// TODO
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { ActionConfig } from "custom-card-helpers";
|
||||||
|
|
||||||
|
// TODO Add your configuration elements here for type-checking
|
||||||
|
export interface BoilerplateConfig {
|
||||||
|
type: string;
|
||||||
|
name?: string;
|
||||||
|
show_warning?: boolean;
|
||||||
|
show_error?: boolean;
|
||||||
|
entity?: string;
|
||||||
|
tap_aciton?: ActionConfig;
|
||||||
|
hold_action?: ActionConfig;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user