custom-card-helpers
This commit is contained in:
@@ -12,6 +12,10 @@ rules:
|
||||
no-nested-ternary: 0
|
||||
camelcase: 0
|
||||
no-unused-vars: 0
|
||||
quotes: 0
|
||||
comma-dangle: 0
|
||||
import/no-unresolved: 0
|
||||
import/prefer-default-export: 0
|
||||
globals:
|
||||
window: true
|
||||
Event: true
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
/node_modules/
|
||||
/.rpt2_cache/
|
||||
yarn.lock
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 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]
|
||||
[![License][license-shield]](LICENSE.md)
|
||||
@@ -12,27 +13,45 @@ A community driven boilerplate of best practices from Home Assistant Lovelace cu
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Type | Requirement | Description
|
||||
| ---- | ---- | ------- | -----------
|
||||
| Name | Type | Requirement | Description | Default
|
||||
| ---- | ---- | ------- | ----------- | -------
|
||||
| type | string | **Required** | `custom:boilerplate-card`
|
||||
| name | string | **Optional** | Card name
|
||||
| show_error | boolean | **Optional** | Show what an error looks like for the card
|
||||
| show_warning | boolean | **Optional** | Show what a warning looks like for the card
|
||||
| name | string | **Optional** | Card name | `Boilerplate`
|
||||
| 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 | `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
|
||||
|
||||
### Step 1
|
||||
|
||||
Clone this repo
|
||||
|
||||
### Step 2
|
||||
|
||||
Install necessary modules
|
||||
`yarn install` or `npm install`
|
||||
|
||||
### Step 3
|
||||
|
||||
Do a test lint & build on the project. You can see available scripts in the package.json
|
||||
`npm run build`
|
||||
|
||||
### Step 4
|
||||
|
||||
Customize to suit your needs and contribute it back to the custom-cards org
|
||||
|
||||
[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, {
|
||||
// tslint:disable-next-line:no-any no symbol in index
|
||||
get() {
|
||||
// tslint:disable-next-line:no-any no symbol in index
|
||||
return this[key];
|
||||
},
|
||||
set(value) {
|
||||
@@ -1551,7 +1550,7 @@ class UpdatingElement extends HTMLElement {
|
||||
const oldValue = this[name];
|
||||
// tslint:disable-next-line:no-any no symbol in index
|
||||
this[key] = value;
|
||||
this.requestUpdate(name, oldValue);
|
||||
this._requestUpdate(name, oldValue);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
@@ -1655,6 +1654,8 @@ class UpdatingElement extends HTMLElement {
|
||||
*/
|
||||
initialize() {
|
||||
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.
|
||||
@@ -1695,7 +1696,7 @@ class UpdatingElement extends HTMLElement {
|
||||
}
|
||||
connectedCallback() {
|
||||
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`
|
||||
// will exist. If so, resolve it to complete the update, otherwise
|
||||
// requestUpdate.
|
||||
@@ -1703,9 +1704,6 @@ class UpdatingElement extends HTMLElement {
|
||||
this._hasConnectedResolver();
|
||||
this._hasConnectedResolver = undefined;
|
||||
}
|
||||
else {
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 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
|
||||
* 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.
|
||||
*/
|
||||
requestUpdate(name, oldValue) {
|
||||
let shouldRequestUpdate = true;
|
||||
// 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();
|
||||
}
|
||||
this._requestUpdate(name, oldValue);
|
||||
return this.updateComplete;
|
||||
}
|
||||
/**
|
||||
@@ -1817,22 +1827,36 @@ class UpdatingElement extends HTMLElement {
|
||||
// Mark state updating...
|
||||
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
|
||||
let resolve;
|
||||
let reject;
|
||||
const previousUpdatePromise = this._updatePromise;
|
||||
this._updatePromise = new Promise((res) => resolve = res);
|
||||
// Ensure any previous update has resolved before updating.
|
||||
// This `await` also ensures that property changes are batched.
|
||||
await previousUpdatePromise;
|
||||
this._updatePromise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
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.
|
||||
if (!this._hasConnected) {
|
||||
await new Promise((res) => this._hasConnectedResolver = res);
|
||||
}
|
||||
// Allow `performUpdate` to be asynchronous to enable scheduling of updates.
|
||||
const result = this.performUpdate();
|
||||
// Note, this is to avoid delaying an additional microtask unless we need
|
||||
// to.
|
||||
if (result != null &&
|
||||
typeof result.then === 'function') {
|
||||
await result;
|
||||
try {
|
||||
const result = this.performUpdate();
|
||||
// If `performUpdate` returns a Promise, we await it. This is done to
|
||||
// enable coordinating updates with a scheduler. Note, the result is
|
||||
// checked to avoid delaying an additional microtask unless we need to.
|
||||
if (result != null) {
|
||||
await result;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
resolve(!this._hasRequestedUpdate);
|
||||
}
|
||||
@@ -1846,10 +1870,13 @@ class UpdatingElement extends HTMLElement {
|
||||
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,
|
||||
* to schedule updates to occur just before the next frame:
|
||||
* You can override this method to change the timing of updates. If this
|
||||
* 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> {
|
||||
@@ -1863,19 +1890,31 @@ class UpdatingElement extends HTMLElement {
|
||||
if (this._instanceProperties) {
|
||||
this._applyInstanceProperties();
|
||||
}
|
||||
if (this.shouldUpdate(this._changedProperties)) {
|
||||
const changedProperties = this._changedProperties;
|
||||
this.update(changedProperties);
|
||||
let shouldUpdate = false;
|
||||
const changedProperties = this._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();
|
||||
}
|
||||
if (shouldUpdate) {
|
||||
if (!(this._updateState & STATE_HAS_UPDATED)) {
|
||||
this._updateState = this._updateState | STATE_HAS_UPDATED;
|
||||
this.firstUpdated(changedProperties);
|
||||
}
|
||||
this.updated(changedProperties);
|
||||
}
|
||||
else {
|
||||
this._markUpdated();
|
||||
}
|
||||
}
|
||||
_markUpdated() {
|
||||
this._changedProperties = new Map();
|
||||
@@ -1885,7 +1924,8 @@ class UpdatingElement extends HTMLElement {
|
||||
* 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
|
||||
* 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
|
||||
* rendered element before fulfilling this Promise. To do this, first await
|
||||
* `super.updateComplete` then any subsequent state.
|
||||
@@ -2196,7 +2236,8 @@ class LitElement extends UpdatingElement {
|
||||
*/
|
||||
initialize() {
|
||||
super.initialize();
|
||||
this.renderRoot = this.createRenderRoot();
|
||||
this.renderRoot =
|
||||
this.createRenderRoot();
|
||||
// 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
|
||||
// support this now since it would require different logic around de-duping.
|
||||
@@ -2302,15 +2343,38 @@ LitElement.finalized = true;
|
||||
*/
|
||||
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
|
||||
let BoilerplateCard = class BoilerplateCard extends LitElement {
|
||||
setConfig(config) {
|
||||
// TODO Check for required fields and that they are of the proper format
|
||||
if (!config || config.show_error) {
|
||||
throw new Error('Invalid configuration');
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
shouldUpdate(changedProps) {
|
||||
return _(this, changedProps);
|
||||
}
|
||||
render() {
|
||||
if (!this._config || !this.hass) {
|
||||
return html ``;
|
||||
@@ -2324,9 +2388,20 @@ let BoilerplateCard = class BoilerplateCard extends LitElement {
|
||||
`;
|
||||
}
|
||||
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() {
|
||||
return css `
|
||||
.warning {
|
||||
@@ -2345,5 +2420,5 @@ __decorate([
|
||||
property()
|
||||
], BoilerplateCard.prototype, "_config", void 0);
|
||||
BoilerplateCard = __decorate([
|
||||
customElement('boilerplate-card')
|
||||
customElement("boilerplate-card")
|
||||
], BoilerplateCard);
|
||||
+9
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boilerplate-card",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "Lovelace boilerplate-card",
|
||||
"keywords": [
|
||||
"home-assistant",
|
||||
@@ -15,16 +15,22 @@
|
||||
"author": "BoilerPlate <boilerplate@email.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"custom-card-helpers": "^1.0.8",
|
||||
"home-assistant-js-websocket": "^3.4.0",
|
||||
"lit-element": "^2.0.1"
|
||||
},
|
||||
"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",
|
||||
"eslint": "^5.14.1",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-typescript": "^0.14.0",
|
||||
"prettier": "^1.16.4",
|
||||
"rollup": "^1.2.3",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-node-resolve": "^4.0.1",
|
||||
"rollup-plugin-typescript2": "^0.19.2",
|
||||
"typescript": "^3.3.3333"
|
||||
@@ -32,7 +38,7 @@
|
||||
"scripts": {
|
||||
"start": "rollup -c --watch",
|
||||
"build": "npm run lint && npm run rollup",
|
||||
"lint": "eslint src/index.ts",
|
||||
"lint": "eslint src/*.ts",
|
||||
"rollup": "rollup -c"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-24
@@ -1,27 +1,17 @@
|
||||
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';
|
||||
|
||||
|
||||
const commonPlugins = [
|
||||
export default {
|
||||
input: ['src/card.ts'],
|
||||
output: {
|
||||
dir: './dist',
|
||||
format: 'es',
|
||||
},
|
||||
plugins: [
|
||||
resolve(),
|
||||
typescript()
|
||||
];
|
||||
|
||||
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]
|
||||
}
|
||||
]
|
||||
typescript(),
|
||||
babel({
|
||||
exclude: 'node_modules/**'
|
||||
})],
|
||||
};
|
||||
|
||||
+30
-12
@@ -6,33 +6,38 @@ import {
|
||||
CSSResult,
|
||||
TemplateResult,
|
||||
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
|
||||
interface BoilerplateConfig {
|
||||
type: string;
|
||||
name?: string;
|
||||
show_warning?: boolean;
|
||||
show_error?: boolean;
|
||||
}
|
||||
import { BoilerplateConfig } from "./types";
|
||||
|
||||
// TODO Name your custom element
|
||||
@customElement('boilerplate-card')
|
||||
@customElement("boilerplate-card")
|
||||
class BoilerplateCard extends LitElement {
|
||||
// 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;
|
||||
|
||||
public setConfig(config: BoilerplateConfig): void {
|
||||
// TODO Check for required fields and that they are of the proper format
|
||||
if (!config || config.show_error) {
|
||||
throw new Error('Invalid configuration');
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
return hasConfigOrEntityChanged(this, changedProps);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._config || !this.hass) {
|
||||
return html``;
|
||||
@@ -48,10 +53,23 @@ class BoilerplateCard extends LitElement {
|
||||
}
|
||||
|
||||
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 {
|
||||
return css`
|
||||
.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