diff --git a/.devcontainer/configuration.yaml b/.devcontainer/configuration.yaml new file mode 100644 index 0000000..9b01ded --- /dev/null +++ b/.devcontainer/configuration.yaml @@ -0,0 +1,4 @@ +default_config: +lovelace: + mode: yaml +demo: \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..d1e7384 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +// See https://aka.ms/vscode-remote/devcontainer.json for format details. +{ + "name": "Restriction Card Development", + "image": "ludeeus/devcontainer:monster-stable", + "context": "..", + "appPort": ["5000:5000", "9123:8123"], + "postCreateCommand": "npm install", + "runArgs": [ + "-v", + "${env:HOME}${env:USERPROFILE}/.ssh:/tmp/.ssh" // This is added so you can push from inside the container + ], + "extensions": [ + "github.vscode-pull-request-github", + "tabnine.tabnine-vscode", + "dbaeumer.vscode-eslint", + "ms-vscode.vscode-typescript-tslint-plugin", + "esbenp.prettier-vscode", + "bierner.lit-html", + "runem.lit-plugin", + "ms-python.python" + ], + "settings": { + "files.eol": "\n", + "editor.tabSize": 4, + "terminal.integrated.shell.linux": "/bin/bash", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } +} diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml new file mode 100644 index 0000000..fe6b3e3 --- /dev/null +++ b/.devcontainer/ui-lovelace.yaml @@ -0,0 +1,67 @@ +resources: + - url: http://127.0.0.1:5000/restriction-card.js + type: module +views: + - cards: + - type: custom:restriction-card + card: + type: light + entity: light.kitchen_lights + name: Basic + - type: custom:hui-entities-card + name: Rows + entities: + - type: custom:restriction-card + row: true + card: + entity: light.kitchen_lights + name: Basic + - type: custom:restriction-card + row: true + card: + entity: light.kitchen_lights + name: Block + restrictions: + block: true + - type: custom:restriction-card + row: true + card: + entity: light.kitchen_lights + name: Confirmation + restrictions: + confirm: true + - type: custom:restriction-card + row: true + card: + entity: light.kitchen_lights + name: Pin + restrictions: + pin: + code: 1234 + - type: custom:restriction-card + restrictions: + confirm: + exemptions: + - user: adminid + pin: + code: 1234 + exemptions: + - user: wifeid + - user: adminid + block: + exemptions: + - user: guestid + - user: wifeid + - user: adminid + exemptions: + - user: ianid + card: + type: light + entity: light.kitchen_lights + name: Exemptions + - type: custom:restriction-card + card: + type: sensor + entity: sensor.outside_humidity + name: Throttle? + graph: line diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..fc69b8b --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier + 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + experimentalDecorators: true, + }, + rules: { + "@typescript-eslint/camelcase": 0 + } +}; diff --git a/.eslintrc.yaml b/.eslintrc.yaml deleted file mode 100644 index 5ba5687..0000000 --- a/.eslintrc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -extends: airbnb-base -parser: "@typescript-eslint/parser" -rules: - no-else-return: 0 - no-underscore-dangle: 0 - nonblock-statement-body-position: 0 - curly: 0 - no-return-assign: 0 - consistent-return: 0 - no-mixed-operators: 0 - class-methods-use-this: 0 - 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 - customElements: true diff --git a/.gitignore b/.gitignore index bfa633e..093889d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ /.rpt2_cache/ +package-lock.json diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..93f1e1c --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + semi: true, + trailingComma: 'all', + singleQuote: true, + printWidth: 120, + tabWidth: 2, +}; \ No newline at end of file diff --git a/dist/restriction-card.js b/dist/restriction-card.js index 8c597e4..da26c1c 100644 --- a/dist/restriction-card.js +++ b/dist/restriction-card.js @@ -25,7 +25,7 @@ function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPro * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt - */}const e=new WeakMap,i=t=>(...i)=>{const n=t(...i);return e.set(n,!0),n},n=t=>"function"==typeof t&&e.has(t),s=void 0!==window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,o=(t,e,i=null)=>{for(;e!==i;){const i=e.nextSibling;t.removeChild(e),e=i}},r={},a={},c=`{{lit-${String(Math.random()).slice(2)}}}`,l=`\x3c!--${c}--\x3e`,d=new RegExp(`${c}|${l}`),h="$lit$";class u{constructor(t,e){this.parts=[],this.element=e;const i=[],n=[],s=document.createTreeWalker(e.content,133,null,!1);let o=0,r=-1,a=0;const{strings:l,values:{length:u}}=t;for(;a0;){const e=l[a],i=g.exec(e)[2],n=i.toLowerCase()+h,s=t.getAttribute(n);t.removeAttribute(n);const o=s.split(d);this.parts.push({type:"attribute",index:r,name:i,strings:o}),a+=o.length-1}}"TEMPLATE"===t.tagName&&(n.push(t),s.currentNode=t.content)}else if(3===t.nodeType){const e=t.data;if(e.indexOf(c)>=0){const n=t.parentNode,s=e.split(d),o=s.length-1;for(let e=0;e{const i=t.length-e.length;return i>=0&&t.slice(i)===e},m=t=>-1!==t.index,f=()=>document.createComment(""),g=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/; + */}const e=new WeakMap,i=t=>(...i)=>{const n=t(...i);return e.set(n,!0),n},n=t=>"function"==typeof t&&e.has(t),s=void 0!==window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,o=(t,e,i=null)=>{for(;e!==i;){const i=e.nextSibling;t.removeChild(e),e=i}},r={},a={},c=`{{lit-${String(Math.random()).slice(2)}}}`,l=`\x3c!--${c}--\x3e`,h=new RegExp(`${c}|${l}`),d="$lit$";class u{constructor(t,e){this.parts=[],this.element=e;const i=[],n=[],s=document.createTreeWalker(e.content,133,null,!1);let o=0,r=-1,a=0;const{strings:l,values:{length:u}}=t;for(;a0;){const e=l[a],i=g.exec(e)[2],n=i.toLowerCase()+d,s=t.getAttribute(n);t.removeAttribute(n);const o=s.split(h);this.parts.push({type:"attribute",index:r,name:i,strings:o}),a+=o.length-1}}"TEMPLATE"===t.tagName&&(n.push(t),s.currentNode=t.content)}else if(3===t.nodeType){const e=t.data;if(e.indexOf(c)>=0){const n=t.parentNode,s=e.split(h),o=s.length-1;for(let e=0;e{const i=t.length-e.length;return i>=0&&t.slice(i)===e},m=t=>-1!==t.index,f=()=>document.createComment(""),g=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/; /** * @license * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. @@ -52,7 +52,7 @@ class _{constructor(t,e,i){this.__parts=[],this.template=t,this.processor=e,this * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt - */const y=` ${c} `;class v{constructor(t,e,i,n){this.strings=t,this.values=e,this.type=i,this.processor=n}getHTML(){const t=this.strings.length-1;let e="",i=!1;for(let n=0;n-1||i)&&-1===t.indexOf("--\x3e",s+1);const o=g.exec(t);e+=null===o?t+(i?y:l):t.substr(0,o.index)+o[1]+o[2]+h+o[3]+c}return e+=this.strings[t]}getTemplateElement(){const t=document.createElement("template");return t.innerHTML=this.getHTML(),t}} + */const y=` ${c} `;class v{constructor(t,e,i,n){this.strings=t,this.values=e,this.type=i,this.processor=n}getHTML(){const t=this.strings.length-1;let e="",i=!1;for(let n=0;n-1||i)&&-1===t.indexOf("--\x3e",s+1);const o=g.exec(t);e+=null===o?t+(i?y:l):t.substr(0,o.index)+o[1]+o[2]+d+o[3]+c}return e+=this.strings[t]}getTemplateElement(){const t=document.createElement("template");return t.innerHTML=this.getHTML(),t}} /** * @license * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. @@ -131,7 +131,7 @@ class _{constructor(t,e,i){this.__parts=[],this.template=t,this.processor=e,this * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt - */function H(t,e){const{element:{content:i},parts:n}=t,s=document.createTreeWalker(i,Y,null,!1);let o=U(n),r=n[o],a=-1,c=0;const l=[];let d=null;for(;s.nextNode();){a++;const t=s.currentNode;for(t.previousSibling===d&&(d=null),e.has(t)&&(l.push(t),null===d&&(d=t)),null!==d&&c++;void 0!==r&&r.index===a;)r.index=null!==d?-1:r.index-c,r=n[o=U(n,o)]}l.forEach(t=>t.parentNode.removeChild(t))}const L=t=>{let e=11===t.nodeType?0:1;const i=document.createTreeWalker(t,Y,null,!1);for(;i.nextNode();)e++;return e},U=(t,e=-1)=>{for(let i=e+1;it.parentNode.removeChild(t))}const L=t=>{let e=11===t.nodeType?0:1;const i=document.createTreeWalker(t,Y,null,!1);for(;i.nextNode();)e++;return e},U=(t,e=-1)=>{for(let i=e+1;it.flat?t.flat(1/0):function t(e,i=[]){for(let n=0,s=e.length;n(t.add(e),t),new Set).forEach(t=>e.unshift(t))}else t&&e.push(t);return e}initialize(){super.initialize(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow({mode:"open"})}adoptStyles(){const t=this.constructor._styles;0!==t.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?rt?this.renderRoot.adoptedStyleSheets=t.map(t=>t.styleSheet):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(t.map(t=>t.cssText),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(t){super.update(t);const e=this.render();e instanceof v&&this.constructor.render(e,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach(t=>{const e=document.createElement("style");e.textContent=t.cssText,this.renderRoot.appendChild(e)}))}render(){}}ht.finalized=!0,ht.render=(t,e,i)=>{if(!i||"object"!=typeof i||!i.scopeName)throw new Error("The `scopeName` option is required.");const n=i.scopeName,s=V.has(e),r=z&&11===e.nodeType&&!!e.host,a=r&&!I.has(n),c=a?document.createDocumentFragment():e;if(((t,e,i)=>{let n=V.get(e);void 0===n&&(o(e,e.firstChild),V.set(e,n=new P(Object.assign({templateFactory:D},i))),n.appendInto(e)),n.setValue(t),n.commit()})(t,c,Object.assign({templateFactory:$(n)},i)),a){const t=V.get(c);V.delete(c);const i=t.value instanceof _?t.value.template:void 0;q(n,c,i),o(e,e.firstChild),e.appendChild(c),V.set(e,t)}!s&&r&&window.ShadyCSS.styleElement(e.host)}; +(window.litElementVersions||(window.litElementVersions=[])).push("2.2.1");const ht=t=>t.flat?t.flat(1/0):function t(e,i=[]){for(let n=0,s=e.length;n(t.add(e),t),new Set).forEach(t=>e.unshift(t))}else t&&e.push(t);return e}initialize(){super.initialize(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow({mode:"open"})}adoptStyles(){const t=this.constructor._styles;0!==t.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?rt?this.renderRoot.adoptedStyleSheets=t.map(t=>t.styleSheet):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(t.map(t=>t.cssText),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(t){super.update(t);const e=this.render();e instanceof v&&this.constructor.render(e,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach(t=>{const e=document.createElement("style");e.textContent=t.cssText,this.renderRoot.appendChild(e)}))}render(){}}dt.finalized=!0,dt.render=(t,e,i)=>{if(!i||"object"!=typeof i||!i.scopeName)throw new Error("The `scopeName` option is required.");const n=i.scopeName,s=V.has(e),r=z&&11===e.nodeType&&!!e.host,a=r&&!I.has(n),c=a?document.createDocumentFragment():e;if(((t,e,i)=>{let n=V.get(e);void 0===n&&(o(e,e.firstChild),V.set(e,n=new P(Object.assign({templateFactory:D},i))),n.appendInto(e)),n.setValue(t),n.commit()})(t,c,Object.assign({templateFactory:$(n)},i)),a){const t=V.get(c);V.delete(c);const i=t.value instanceof _?t.value.template:void 0;q(n,c,i),o(e,e.firstChild),e.appendChild(c),V.set(e,t)}!s&&r&&window.ShadyCSS.styleElement(e.host)}; /** * @license * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. @@ -197,9 +197,9 @@ found at http://polymer.github.io/PATENTS.txt * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ -const ut=new WeakMap,pt=i(t=>e=>{if(!(e instanceof x)||e instanceof N||"class"!==e.committer.name||e.committer.parts.length>1)throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const{committer:i}=e,{element:n}=i;ut.has(e)||(n.className=i.strings.join(" "));const{classList:s}=n,o=ut.get(e);for(const e in o)e in t||s.remove(e);for(const e in t){const i=t[e];if(!o||i!==o[e]){s[i?"add":"remove"](e)}}ut.set(e,t)});var mt={},ft=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,gt="[^\\s]+",_t=/\[([^]*?)\]/gm,yt=function(){};function vt(t,e){for(var i=[],n=0,s=t.length;n3?0:(t-t%10!=10)*t%10]}};var kt={D:function(t){return t.getDate()},DD:function(t){return St(t.getDate())},Do:function(t,e){return e.DoFn(t.getDate())},d:function(t){return t.getDay()},dd:function(t){return St(t.getDay())},ddd:function(t,e){return e.dayNamesShort[t.getDay()]},dddd:function(t,e){return e.dayNames[t.getDay()]},M:function(t){return t.getMonth()+1},MM:function(t){return St(t.getMonth()+1)},MMM:function(t,e){return e.monthNamesShort[t.getMonth()]},MMMM:function(t,e){return e.monthNames[t.getMonth()]},YY:function(t){return St(String(t.getFullYear()),4).substr(2)},YYYY:function(t){return St(t.getFullYear(),4)},h:function(t){return t.getHours()%12||12},hh:function(t){return St(t.getHours()%12||12)},H:function(t){return t.getHours()},HH:function(t){return St(t.getHours())},m:function(t){return t.getMinutes()},mm:function(t){return St(t.getMinutes())},s:function(t){return t.getSeconds()},ss:function(t){return St(t.getSeconds())},S:function(t){return Math.round(t.getMilliseconds()/100)},SS:function(t){return St(Math.round(t.getMilliseconds()/10),2)},SSS:function(t){return St(t.getMilliseconds(),3)},a:function(t,e){return t.getHours()<12?e.amPm[0]:e.amPm[1]},A:function(t,e){return t.getHours()<12?e.amPm[0].toUpperCase():e.amPm[1].toUpperCase()},ZZ:function(t){var e=t.getTimezoneOffset();return(e>0?"-":"+")+St(100*Math.floor(Math.abs(e)/60)+Math.abs(e)%60,4)}},Nt={D:["\\d\\d?",function(t,e){t.day=e}],Do:["\\d\\d?"+gt,function(t,e){t.day=parseInt(e,10)}],M:["\\d\\d?",function(t,e){t.month=e-1}],YY:["\\d\\d?",function(t,e){var i=+(""+(new Date).getFullYear()).substr(0,2);t.year=""+(e>68?i-1:i)+e}],h:["\\d\\d?",function(t,e){t.hour=e}],m:["\\d\\d?",function(t,e){t.minute=e}],s:["\\d\\d?",function(t,e){t.second=e}],YYYY:["\\d{4}",function(t,e){t.year=e}],S:["\\d",function(t,e){t.millisecond=100*e}],SS:["\\d{2}",function(t,e){t.millisecond=10*e}],SSS:["\\d{3}",function(t,e){t.millisecond=e}],d:["\\d\\d?",yt],ddd:[gt,yt],MMM:[gt,wt("monthNamesShort")],MMMM:[gt,wt("monthNames")],a:[gt,function(t,e,i){var n=e.toLowerCase();n===i.amPm[0]?t.isPm=!1:n===i.amPm[1]&&(t.isPm=!0)}],ZZ:["[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z",function(t,e){var i,n=(e+"").match(/([+-]|\d\d)/gi);n&&(i=60*n[1]+parseInt(n[2],10),t.timezoneOffset="+"===n[0]?i:-i)}]};Nt.dd=Nt.d,Nt.dddd=Nt.ddd,Nt.DD=Nt.D,Nt.mm=Nt.m,Nt.hh=Nt.H=Nt.HH=Nt.h,Nt.MM=Nt.M,Nt.ss=Nt.s,Nt.A=Nt.a,mt.masks={default:"ddd MMM DD YYYY HH:mm:ss",shortDate:"M/D/YY",mediumDate:"MMM D, YYYY",longDate:"MMMM D, YYYY",fullDate:"dddd, MMMM D, YYYY",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"},mt.format=function(t,e,i){var n=i||mt.i18n;if("number"==typeof t&&(t=new Date(t)),"[object Date]"!==Object.prototype.toString.call(t)||isNaN(t.getTime()))throw new Error("Invalid Date in fecha.format");e=mt.masks[e]||e||mt.masks.default;var s=[];return(e=(e=e.replace(_t,(function(t,e){return s.push(e),"@@@"}))).replace(ft,(function(e){return e in kt?kt[e](t,n):e.slice(1,e.length-1)}))).replace(/@@@/g,(function(){return s.shift()}))},mt.parse=function(t,e,i){var n=i||mt.i18n;if("string"!=typeof e)throw new Error("Invalid format in fecha.parse");if(e=mt.masks[e]||e,t.length>1e3)return null;var s={},o=[],r=[];e=e.replace(_t,(function(t,e){return r.push(e),"@@@"}));var a,c=(a=e,a.replace(/[|\\{()[^$+*?.-]/g,"\\$&")).replace(ft,(function(t){if(Nt[t]){var e=Nt[t];return o.push(e[1]),"("+e[0]+")"}return t}));c=c.replace(/@@@/g,(function(){return r.shift()}));var l=t.match(new RegExp(c,"i"));if(!l)return null;for(var d=1;d=":return n>=i;case">":return n>i;case"!=":return n!==i;case"regex":return n.match(i);default:return!1}};const Dt="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0;class Rt extends HTMLElement{constructor(){super(),this.holdTime=500,this.ripple=document.createElement("mwc-ripple"),this.timer=void 0,this.held=!1,this.cooldownStart=!1,this.cooldownEnd=!1}connectedCallback(){Object.assign(this.style,{position:"absolute",width:Dt?"100px":"50px",height:Dt?"100px":"50px",transform:"translate(-50%, -50%)",pointerEvents:"none"}),this.appendChild(this.ripple),this.ripple.primary=!0,["touchcancel","mouseout","mouseup","touchmove","mousewheel","wheel","scroll"].forEach(t=>{document.addEventListener(t,()=>{clearTimeout(this.timer),this.stopAnimation(),this.timer=void 0},{passive:!0})})}bind(t,e){if(t.actionHandler)return;t.actionHandler=!0,t.addEventListener("contextmenu",t=>{const e=t||window.event;return e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0,e.returnValue=!1,!1});const i=t=>{if(this.cooldownStart)return;let i,n;this.held=!1,t.touches?(i=t.touches[0].pageX,n=t.touches[0].pageY):(i=t.pageX,n=t.pageY),e.hasHold&&(this.timer=window.setTimeout(()=>{this.startAnimation(i,n),this.held=!0},this.holdTime)),this.cooldownStart=!0,window.setTimeout(()=>this.cooldownStart=!1,100)},n=i=>{this.cooldownEnd||["touchend","touchcancel"].includes(i.type)&&void 0===this.timer||(clearTimeout(this.timer),this.stopAnimation(),this.timer=void 0,this.held?Et(t,"action",{action:"hold"}):e.hasDoubleTap?1===i.detail?this.dblClickTimeout=window.setTimeout(()=>{Et(t,"action",{action:"tap"})},250):(clearTimeout(this.dblClickTimeout),Et(t,"action",{action:"double_tap"})):Et(t,"action",{action:"tap"}),this.cooldownEnd=!0,window.setTimeout(()=>this.cooldownEnd=!1,100))};t.addEventListener("touchstart",i,{passive:!0}),t.addEventListener("touchend",n),t.addEventListener("touchcancel",n),window.navigator.userAgent.match(/iPhone OS 13_/)||(t.addEventListener("mousedown",i,{passive:!0}),t.addEventListener("click",n))}startAnimation(t,e){Object.assign(this.style,{left:`${t}px`,top:`${e}px`,display:null}),this.ripple.disabled=!1,this.ripple.active=!0,this.ripple.unbounded=!0}stopAnimation(){this.ripple.active=!1,this.ripple.disabled=!0,this.style.display="none"}}customElements.define("action-handler-restriction",Rt);const Vt=(t,e)=>{const i=(()=>{const t=document.body;if(t.querySelector("action-handler-restriction"))return t.querySelector("action-handler-restriction");const e=document.createElement("action-handler-restriction");return t.appendChild(e),e})();i&&i.bind(t,e)},Ot=i((t={})=>e=>{Vt(e.committer.element,t)});console.info("%c RESTRICTION-CARD \n%c Version 1.1.2 ","color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray");let Yt=class extends ht{set hass(t){this._hass=t;const e=this.shadowRoot.querySelector("#card > *");e&&(e.hass=t)}getCardSize(){const t=this.shadowRoot.querySelector("#card > *");return t&&"function"==typeof(e=t).getCardSize?e.getCardSize():1;var e}setConfig(t){if(!t.card)throw new Error("Error in card configuration.");if(t.restrictions&&t.restrictions.pin&&!t.restrictions.pin.code)throw new Error("A pin code is required for pin restrictions");this._config=Object.assign({duration:5,action:"tap"},t)}shouldUpdate(t){const e=t.get("hass");return!(!t.has("config")&&e)||!!(this._config&&this._config.condition&&this._config.condition.entity)&&e.states[this._config.condition.entity]!==this._hass.states[this._config.condition.entity]}render(){return this._config&&this._hass?this._config.restrictions&&this._matchRestriction(this._config.restrictions.hide)?O``:O` +const ut=new WeakMap,pt=i(t=>e=>{if(!(e instanceof x)||e instanceof N||"class"!==e.committer.name||e.committer.parts.length>1)throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const{committer:i}=e,{element:n}=i;ut.has(e)||(n.className=i.strings.join(" "));const{classList:s}=n,o=ut.get(e);for(const e in o)e in t||s.remove(e);for(const e in t){const i=t[e];if(!o||i!==o[e]){s[i?"add":"remove"](e)}}ut.set(e,t)});var mt={},ft=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,gt="[^\\s]+",_t=/\[([^]*?)\]/gm,yt=function(){};function vt(t,e){for(var i=[],n=0,s=t.length;n3?0:(t-t%10!=10)*t%10]}};var kt={D:function(t){return t.getDate()},DD:function(t){return St(t.getDate())},Do:function(t,e){return e.DoFn(t.getDate())},d:function(t){return t.getDay()},dd:function(t){return St(t.getDay())},ddd:function(t,e){return e.dayNamesShort[t.getDay()]},dddd:function(t,e){return e.dayNames[t.getDay()]},M:function(t){return t.getMonth()+1},MM:function(t){return St(t.getMonth()+1)},MMM:function(t,e){return e.monthNamesShort[t.getMonth()]},MMMM:function(t,e){return e.monthNames[t.getMonth()]},YY:function(t){return St(String(t.getFullYear()),4).substr(2)},YYYY:function(t){return St(t.getFullYear(),4)},h:function(t){return t.getHours()%12||12},hh:function(t){return St(t.getHours()%12||12)},H:function(t){return t.getHours()},HH:function(t){return St(t.getHours())},m:function(t){return t.getMinutes()},mm:function(t){return St(t.getMinutes())},s:function(t){return t.getSeconds()},ss:function(t){return St(t.getSeconds())},S:function(t){return Math.round(t.getMilliseconds()/100)},SS:function(t){return St(Math.round(t.getMilliseconds()/10),2)},SSS:function(t){return St(t.getMilliseconds(),3)},a:function(t,e){return t.getHours()<12?e.amPm[0]:e.amPm[1]},A:function(t,e){return t.getHours()<12?e.amPm[0].toUpperCase():e.amPm[1].toUpperCase()},ZZ:function(t){var e=t.getTimezoneOffset();return(e>0?"-":"+")+St(100*Math.floor(Math.abs(e)/60)+Math.abs(e)%60,4)}},Nt={D:["\\d\\d?",function(t,e){t.day=e}],Do:["\\d\\d?"+gt,function(t,e){t.day=parseInt(e,10)}],M:["\\d\\d?",function(t,e){t.month=e-1}],YY:["\\d\\d?",function(t,e){var i=+(""+(new Date).getFullYear()).substr(0,2);t.year=""+(e>68?i-1:i)+e}],h:["\\d\\d?",function(t,e){t.hour=e}],m:["\\d\\d?",function(t,e){t.minute=e}],s:["\\d\\d?",function(t,e){t.second=e}],YYYY:["\\d{4}",function(t,e){t.year=e}],S:["\\d",function(t,e){t.millisecond=100*e}],SS:["\\d{2}",function(t,e){t.millisecond=10*e}],SSS:["\\d{3}",function(t,e){t.millisecond=e}],d:["\\d\\d?",yt],ddd:[gt,yt],MMM:[gt,wt("monthNamesShort")],MMMM:[gt,wt("monthNames")],a:[gt,function(t,e,i){var n=e.toLowerCase();n===i.amPm[0]?t.isPm=!1:n===i.amPm[1]&&(t.isPm=!0)}],ZZ:["[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z",function(t,e){var i,n=(e+"").match(/([+-]|\d\d)/gi);n&&(i=60*n[1]+parseInt(n[2],10),t.timezoneOffset="+"===n[0]?i:-i)}]};Nt.dd=Nt.d,Nt.dddd=Nt.ddd,Nt.DD=Nt.D,Nt.mm=Nt.m,Nt.hh=Nt.H=Nt.HH=Nt.h,Nt.MM=Nt.M,Nt.ss=Nt.s,Nt.A=Nt.a,mt.masks={default:"ddd MMM DD YYYY HH:mm:ss",shortDate:"M/D/YY",mediumDate:"MMM D, YYYY",longDate:"MMMM D, YYYY",fullDate:"dddd, MMMM D, YYYY",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"},mt.format=function(t,e,i){var n=i||mt.i18n;if("number"==typeof t&&(t=new Date(t)),"[object Date]"!==Object.prototype.toString.call(t)||isNaN(t.getTime()))throw new Error("Invalid Date in fecha.format");e=mt.masks[e]||e||mt.masks.default;var s=[];return(e=(e=e.replace(_t,(function(t,e){return s.push(e),"@@@"}))).replace(ft,(function(e){return e in kt?kt[e](t,n):e.slice(1,e.length-1)}))).replace(/@@@/g,(function(){return s.shift()}))},mt.parse=function(t,e,i){var n=i||mt.i18n;if("string"!=typeof e)throw new Error("Invalid format in fecha.parse");if(e=mt.masks[e]||e,t.length>1e3)return null;var s={},o=[],r=[];e=e.replace(_t,(function(t,e){return r.push(e),"@@@"}));var a,c=(a=e,a.replace(/[|\\{()[^$+*?.-]/g,"\\$&")).replace(ft,(function(t){if(Nt[t]){var e=Nt[t];return o.push(e[1]),"("+e[0]+")"}return t}));c=c.replace(/@@@/g,(function(){return r.shift()}));var l=t.match(new RegExp(c,"i"));if(!l)return null;for(var h=1;h=":return n>=i;case">":return n>i;case"!=":return n!==i;case"regex":return n.match(i);default:return!1}};const Dt="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0;class Rt extends HTMLElement{constructor(){super(),this.holdTime=500,this.ripple=document.createElement("mwc-ripple"),this.timer=void 0,this.held=!1,this.cooldownStart=!1,this.cooldownEnd=!1}connectedCallback(){Object.assign(this.style,{position:"absolute",width:Dt?"100px":"50px",height:Dt?"100px":"50px",transform:"translate(-50%, -50%)",pointerEvents:"none"}),this.appendChild(this.ripple),this.ripple.primary=!0,["touchcancel","mouseout","mouseup","touchmove","mousewheel","wheel","scroll"].forEach(t=>{document.addEventListener(t,()=>{clearTimeout(this.timer),this.stopAnimation(),this.timer=void 0},{passive:!0})})}bind(t,e){if(t.actionHandler)return;t.actionHandler=!0,t.addEventListener("contextmenu",t=>{const e=t||window.event;e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0,e.returnValue=!1});const i=t=>{if(this.cooldownStart)return;let i,n;this.held=!1,t.touches?(i=t.touches[0].pageX,n=t.touches[0].pageY):(i=t.pageX,n=t.pageY),e.hasHold&&(this.timer=window.setTimeout(()=>{this.startAnimation(i,n),this.held=!0},this.holdTime)),this.cooldownStart=!0,window.setTimeout(()=>this.cooldownStart=!1,100)},n=i=>{this.cooldownEnd||["touchend","touchcancel"].includes(i.type)&&void 0===this.timer||(clearTimeout(this.timer),this.stopAnimation(),this.timer=void 0,this.held?Et(t,"action",{action:"hold"}):e.hasDoubleTap?1===i.detail||"keyup"===i.type?this.dblClickTimeout=window.setTimeout(()=>{Et(t,"action",{action:"tap"})},250):(clearTimeout(this.dblClickTimeout),Et(t,"action",{action:"double_tap"})):Et(t,"action",{action:"tap"}),this.cooldownEnd=!0,window.setTimeout(()=>this.cooldownEnd=!1,100))};t.addEventListener("touchstart",i,{passive:!0}),t.addEventListener("touchend",n),t.addEventListener("touchcancel",n),t.addEventListener("keyup",t=>{if(13===t.keyCode)return n(t)}),window.navigator.userAgent.match(/iPhone OS 13_/)||(t.addEventListener("mousedown",i,{passive:!0}),t.addEventListener("click",n))}startAnimation(t,e){Object.assign(this.style,{left:`${t}px`,top:`${e}px`,display:null}),this.ripple.disabled=!1,this.ripple.active=!0,this.ripple.unbounded=!0}stopAnimation(){this.ripple.active=!1,this.ripple.disabled=!0,this.style.display="none"}}customElements.define("action-handler-restriction",Rt);const Vt=(t,e)=>{const i=(()=>{const t=document.body;if(t.querySelector("action-handler-restriction"))return t.querySelector("action-handler-restriction");const e=document.createElement("action-handler-restriction");return t.appendChild(e),e})();i&&i.bind(t,e)},Ot=i((t={})=>e=>{Vt(e.committer.element,t)});console.info("%c RESTRICTION-CARD \n%c Version 1.1.4 ","color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray");let Yt=class extends dt{set hass(t){if(this._hass=t,this.shadowRoot){const e=this.shadowRoot.querySelector("#card > *");e&&(e.hass=t)}}getCardSize(){if(this.shadowRoot){const e=this.shadowRoot.querySelector("#card > *");if(e)return"function"==typeof(t=e).getCardSize?t.getCardSize():1}var t;return 1}setConfig(t){if(!t.card)throw new Error("Error in card configuration.");if(t.restrictions&&t.restrictions.pin&&!t.restrictions.pin.code)throw new Error("A pin code is required for pin restrictions");this._config=Object.assign({duration:5,action:"tap"},t)}shouldUpdate(t){const e=t.get("hass");return!(!t.has("config")&&e)||!!(this._hass&&this._config&&this._config.condition&&this._config.condition.entity)&&e.states[this._config.condition.entity]!==this._hass.states[this._config.condition.entity]}render(){return this._config&&this._hass&&this._config.card?this._config.restrictions&&this._matchRestriction(this._config.restrictions.hide)?O``:O`
- ${this._config.exemptions&&this._config.exemptions.some(t=>t.user===this._hass.user.id)||this._config.condition&&!At(this._hass.states[this._config.condition.entity],this._config.condition)?"":O` + ${this._config.exemptions&&this._config.exemptions.some(t=>!(!this._hass||!this._hass.user)&&t.user===this._hass.user.id)||this._config.condition&&!At(this._hass.states[this._config.condition.entity],this._config.condition)?"":O`
e=>{if(!(e instanceof x)||e instanceof N||"class"!= `} ${this.renderCard(this._config.card)}
- `:O``}renderCard(t){const e=function(t,e){void 0===e&&(e=!1);var i=function(t,e){return n("hui-error-card",{type:"error",error:t,config:e})},n=function(t,e){var n=window.document.createElement(t);try{n.setConfig(e)}catch(n){return console.error(t,n),i(n.message,e)}return n};if(!t||"object"!=typeof t||!e&&!t.type)return i("No type defined",t);var s=t.type;if(s&&s.startsWith("custom:"))s=s.substr("custom:".length);else if(e)if(Tt.has(s))s="hui-"+s+"-row";else{if(!t.entity)return i("Invalid config given.",t);var o=t.entity.split(".",1)[0];s="hui-"+(Mt[o]||"text")+"-entity-row"}else s="hui-"+s+"-card";if(customElements.get(s))return n(s,t);var r=i("Custom element doesn't exist: "+t.type+".",t);r.style.display="None";var a=setTimeout((function(){r.style.display=""}),2e3);return customElements.whenDefined(t.type).then((function(){clearTimeout(a),Et(r,"ll-rebuild",{},r)})),r}(t,this._config.row);return this._hass&&(e.hass=this._hass),O` -
- ${e} -
- `}_matchRestriction(t){return t&&(!t.exemptions||!t.exemptions.some(t=>t.user===this._hass.user.id))&&(!t.condition||At(this._hass.states[t.condition.entity],t.condition))}_handleAction(t){this._config.action===t.detail.action&&this._handleRestriction()}_handleRestriction(){const t=this.shadowRoot.getElementById("lock");if(this._config.restrictions){if(this._matchRestriction(this._config.restrictions.block))return this._config.restrictions.block.text&&alert(this._config.restrictions.block.text),t.classList.add("invalid"),void window.setTimeout(()=>{t&&t.classList.remove("invalid")},3e3);if(this._matchRestriction(this._config.restrictions.pin)){if(prompt(this._config.restrictions.pin.text||"Input pin code")!=this._config.restrictions.pin.code)return t.classList.add("invalid"),void window.setTimeout(()=>{t&&t.classList.remove("invalid")},3e3)}if(this._matchRestriction(this._config.restrictions.confirm)&&!confirm(this._config.restrictions.confirm.text||"Are you sure you want to unlock?"))return}const e=this.shadowRoot.getElementById("overlay");e.style.setProperty("pointer-events","none"),t.classList.add("hidden"),window.setTimeout(()=>{e.style.setProperty("pointer-events",""),t&&t.classList.remove("hidden")},1e3*this._config.duration)}static get styles(){return lt` + `:O``}renderCard(t){if(this._hass&&this._config){const e=function(t,e){void 0===e&&(e=!1);var i=function(t,e){return n("hui-error-card",{type:"error",error:t,config:e})},n=function(t,e){var n=window.document.createElement(t);try{n.setConfig(e)}catch(n){return console.error(t,n),i(n.message,e)}return n};if(!t||"object"!=typeof t||!e&&!t.type)return i("No type defined",t);var s=t.type;if(s&&s.startsWith("custom:"))s=s.substr("custom:".length);else if(e)if(Tt.has(s))s="hui-"+s+"-row";else{if(!t.entity)return i("Invalid config given.",t);var o=t.entity.split(".",1)[0];s="hui-"+(Mt[o]||"text")+"-entity-row"}else s="hui-"+s+"-card";if(customElements.get(s))return n(s,t);var r=i("Custom element doesn't exist: "+t.type+".",t);r.style.display="None";var a=setTimeout((function(){r.style.display=""}),2e3);return customElements.whenDefined(t.type).then((function(){clearTimeout(a),Et(r,"ll-rebuild",{},r)})),r}(t,this._config.row);return e.hass=this._hass,O` +
+ ${e} +
+ `}return O``}_matchRestriction(t){return this._hass&&t&&(!t.exemptions||!t.exemptions.some(t=>!(!this._hass||!this._hass.user)&&t.user===this._hass.user.id))&&(!t.condition||At(this._hass.states[t.condition.entity],t.condition))}_handleAction(t){this._config&&this._config.action===t.detail.action&&this._handleRestriction()}_handleRestriction(){if(!this._config||!this.shadowRoot)return;const t=this.shadowRoot.getElementById("lock");if(this._config.restrictions){if(this._config.restrictions.block&&this._matchRestriction(this._config.restrictions.block))return this._config.restrictions.block.text&&alert(this._config.restrictions.block.text),t.classList.add("invalid"),void window.setTimeout(()=>{t&&t.classList.remove("invalid")},3e3);if(this._config.restrictions.pin&&this._matchRestriction(this._config.restrictions.pin)){if(prompt(this._config.restrictions.pin.text||"Input pin code")!=this._config.restrictions.pin.code)return t.classList.add("invalid"),void window.setTimeout(()=>{t&&t.classList.remove("invalid")},3e3)}if(this._config.restrictions.confirm&&this._matchRestriction(this._config.restrictions.confirm)&&!confirm(this._config.restrictions.confirm.text||"Are you sure you want to unlock?"))return}const e=this.shadowRoot.getElementById("overlay");e.style.setProperty("pointer-events","none"),t.classList.add("hidden"),window.setTimeout(()=>{e.style.setProperty("pointer-events",""),t&&t.classList.remove("hidden")},1e3*this._config.duration)}static get styles(){return lt` :host { display: block; position: relative; - --regular-lock-color: var( - --restriction-regular-lock-color, - var(--primary-text-color, #212121) - ); - --success-lock-color: var( - --restriction-success-lock-color, - var(--primary-color, #03a9f4) - ); - --blocked-lock-color: var( - --restriction-blocked-lock-color, - var(--error-state-color, #db4437) - ); - --invalid-lock-color: var( - --restriction-invalid--color, - var(--error-state-color, #db4437) - ); + --regular-lock-color: var(--restriction-regular-lock-color, var(--primary-text-color, #212121)); + --success-lock-color: var(--restriction-success-lock-color, var(--primary-color, #03a9f4)); + --blocked-lock-color: var(--restriction-blocked-lock-color, var(--error-state-color, #db4437)); + --invalid-lock-color: var(--restriction-invalid--color, var(--error-state-color, #db4437)); --lock-margin-left: var(--restriction-lock-margin-left, 0px); --lock-row-margin-left: var(--restriction-lock-row-margin-left, 24px); - --lock-icon-height: var( - --restriction-lock-icon-height, - var(--iron-icon-height, 24px) - ); - --lock-icon-width: var( - --restriction-lock-icon-width, - var(--iron-icon-width, 24px) - ); + --lock-icon-height: var(--restriction-lock-icon-height, var(--iron-icon-height, 24px)); + --lock-icon-width: var(--restriction-lock-icon-width, var(--iron-icon-width, 24px)); } ha-icon { --iron-icon-height: var(--lock-icon-height); diff --git a/package.json b/package.json index a395ec9..d8b36b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "restriction-card", - "version": "1.0.0", + "version": "1.1.4", "description": "Lovelace restriction-card", "keywords": [ "home-assistant", @@ -8,16 +8,18 @@ "hass", "automation", "lovelace", - "custom-cards" + "custom-cards", + "security" ], "module": "restriction-card.js", - "repository": "git@github.com:custom_cards/restriction-card.git", - "author": "BoilerPlate ", + "repository": "git@github.com:iantrich/restriction-card.git", + "author": "Ian Richardson ", "license": "MIT", "dependencies": { - "custom-card-helpers": "^1.3.5", + "custom-card-helpers": "^1.3.9", "home-assistant-js-websocket": "^4.4.0", - "lit-element": "^2.2.1" + "lit-element": "^2.2.1", + "lit-html": "^1.1.2" }, "devDependencies": { "@babel/core": "^7.6.4", @@ -27,18 +29,21 @@ "@typescript-eslint/parser": "^2.6.0", "eslint": "^6.6.0", "eslint-config-airbnb-base": "^14.0.0", + "eslint-config-prettier": "^6.5.0", "eslint-plugin-import": "^2.18.2", + "eslint-plugin-prettier": "^3.1.1", "prettier": "^1.18.2", "rollup": "^1.26.0", "rollup-plugin-babel": "^4.3.3", "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-serve": "^1.0.1", "rollup-plugin-terser": "^5.1.2", "rollup-plugin-typescript2": "^0.24.3", "rollup-plugin-uglify": "^6.0.3", "typescript": "^3.6.4" }, "scripts": { - "start": "rollup -c --watch", + "start": "rollup -c rollup.config.dev.js --watch", "build": "npm run lint && npm run rollup", "lint": "eslint src/*.ts", "rollup": "rollup -c" diff --git a/rollup.config.dev.js b/rollup.config.dev.js new file mode 100644 index 0000000..762274d --- /dev/null +++ b/rollup.config.dev.js @@ -0,0 +1,30 @@ +import resolve from 'rollup-plugin-node-resolve'; +import typescript from 'rollup-plugin-typescript2'; +import babel from 'rollup-plugin-babel'; +import serve from 'rollup-plugin-serve'; +import { terser } from 'rollup-plugin-terser'; + +export default { + input: ['src/restriction-card.ts'], + output: { + dir: './dist', + format: 'es', + }, + plugins: [ + resolve(), + typescript(), + babel({ + exclude: 'node_modules/**', + }), + terser(), + serve({ + contentBase: './dist', + host: '0.0.0.0', + port: 5000, + allowCrossOrigin: true, + headers: { + 'Access-Control-Allow-Origin': '*', + }, + }), + ], +}; diff --git a/rollup.config.js b/rollup.config.js index bd76c73..bfdd773 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,19 +1,20 @@ -import resolve from 'rollup-plugin-node-resolve'; -import typescript from 'rollup-plugin-typescript2'; -import babel from 'rollup-plugin-babel'; +import resolve from "rollup-plugin-node-resolve"; +import typescript from "rollup-plugin-typescript2"; +import babel from "rollup-plugin-babel"; import { terser } from "rollup-plugin-terser"; export default { - input: ['src/restriction-card.ts'], + input: ["src/restriction-card.ts"], output: { - dir: './dist', - format: 'es', + dir: "./dist", + format: "es" }, plugins: [ resolve(), typescript(), babel({ - exclude: 'node_modules/**' + exclude: "node_modules/**" }), - terser()] + terser(), + ] }; diff --git a/src/action-handler-directive.ts b/src/action-handler-directive.ts index da19788..6b71456 100644 --- a/src/action-handler-directive.ts +++ b/src/action-handler-directive.ts @@ -1,10 +1,7 @@ -import { directive, PropertyPart } from "lit-html"; -import { fireEvent, ActionHandlerOptions } from "custom-card-helpers"; +import { directive, PropertyPart } from 'lit-html'; +import { fireEvent, ActionHandlerOptions } from 'custom-card-helpers'; -const isTouch = - "ontouchstart" in window || - navigator.maxTouchPoints > 0 || - navigator.msMaxTouchPoints > 0; +const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; interface ActionHandler extends HTMLElement { holdTime: number; @@ -16,6 +13,7 @@ interface ActionHandlerElement extends Element { class ActionHandler extends HTMLElement implements ActionHandler { public holdTime: number; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ public ripple: any; protected timer: number | undefined; protected held: boolean; @@ -26,34 +24,26 @@ class ActionHandler extends HTMLElement implements ActionHandler { constructor() { super(); this.holdTime = 500; - this.ripple = document.createElement("mwc-ripple"); + this.ripple = document.createElement('mwc-ripple'); this.timer = undefined; this.held = false; this.cooldownStart = false; this.cooldownEnd = false; } - public connectedCallback() { + public connectedCallback(): void { Object.assign(this.style, { - position: "absolute", - width: isTouch ? "100px" : "50px", - height: isTouch ? "100px" : "50px", - transform: "translate(-50%, -50%)", - pointerEvents: "none", + position: 'absolute', + width: isTouch ? '100px' : '50px', + height: isTouch ? '100px' : '50px', + transform: 'translate(-50%, -50%)', + pointerEvents: 'none', }); this.appendChild(this.ripple); this.ripple.primary = true; - [ - "touchcancel", - "mouseout", - "mouseup", - "touchmove", - "mousewheel", - "wheel", - "scroll", - ].forEach((ev) => { + ['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach(ev => { document.addEventListener( ev, () => { @@ -61,18 +51,18 @@ class ActionHandler extends HTMLElement implements ActionHandler { this.stopAnimation(); this.timer = undefined; }, - { passive: true } + { passive: true }, ); }); } - public bind(element: ActionHandlerElement, options) { + public bind(element: ActionHandlerElement, options): void { if (element.actionHandler) { return; } element.actionHandler = true; - element.addEventListener("contextmenu", (ev: Event) => { + element.addEventListener('contextmenu', (ev: Event) => { const e = ev || window.event; if (e.preventDefault) { e.preventDefault(); @@ -82,10 +72,10 @@ class ActionHandler extends HTMLElement implements ActionHandler { } e.cancelBubble = true; e.returnValue = false; - return false; + return; }); - const clickStart = (ev: Event) => { + const clickStart = (ev: Event): void => { if (this.cooldownStart) { return; } @@ -111,38 +101,41 @@ class ActionHandler extends HTMLElement implements ActionHandler { window.setTimeout(() => (this.cooldownStart = false), 100); }; - const clickEnd = (ev: Event) => { - if ( - this.cooldownEnd || - (["touchend", "touchcancel"].includes(ev.type) && - this.timer === undefined) - ) { + const clickEnd = (ev: Event): void => { + if (this.cooldownEnd || (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined)) { return; } clearTimeout(this.timer); this.stopAnimation(); this.timer = undefined; if (this.held) { - fireEvent(element as HTMLElement, "action", { action: "hold" }); + fireEvent(element as HTMLElement, 'action', { action: 'hold' }); } else if (options.hasDoubleTap) { - if ((ev as MouseEvent).detail === 1) { + if ((ev as MouseEvent).detail === 1 || ev.type === 'keyup') { this.dblClickTimeout = window.setTimeout(() => { - fireEvent(element as HTMLElement, "action", { action: "tap" }); + fireEvent(element as HTMLElement, 'action', { action: 'tap' }); }, 250); } else { clearTimeout(this.dblClickTimeout); - fireEvent(element as HTMLElement, "action", { action: "double_tap" }); + fireEvent(element as HTMLElement, 'action', { action: 'double_tap' }); } } else { - fireEvent(element as HTMLElement, "action", { action: "tap" }); + fireEvent(element as HTMLElement, 'action', { action: 'tap' }); } this.cooldownEnd = true; window.setTimeout(() => (this.cooldownEnd = false), 100); }; - element.addEventListener("touchstart", clickStart, { passive: true }); - element.addEventListener("touchend", clickEnd); - element.addEventListener("touchcancel", clickEnd); + const handleEnter = (ev: Event): void => { + if ((ev as KeyboardEvent).keyCode === 13) { + return clickEnd(ev); + } + }; + + element.addEventListener('touchstart', clickStart, { passive: true }); + element.addEventListener('touchend', clickEnd); + element.addEventListener('touchcancel', clickEnd); + element.addEventListener('keyup', handleEnter); // iOS 13 sends a complete normal touchstart-touchend series of events followed by a mousedown-click series. // That might be a bug, but until it's fixed, this should make action-handler work. @@ -150,12 +143,12 @@ class ActionHandler extends HTMLElement implements ActionHandler { // Note that all events (both touch and mouse) must be listened for in order to work on computers with both mouse and touchscreen. const isIOS13 = window.navigator.userAgent.match(/iPhone OS 13_/); if (!isIOS13) { - element.addEventListener("mousedown", clickStart, { passive: true }); - element.addEventListener("click", clickEnd); + element.addEventListener('mousedown', clickStart, { passive: true }); + element.addEventListener('click', clickEnd); } } - private startAnimation(x: number, y: number) { + private startAnimation(x: number, y: number): void { Object.assign(this.style, { left: `${x}px`, top: `${y}px`, @@ -166,31 +159,28 @@ class ActionHandler extends HTMLElement implements ActionHandler { this.ripple.unbounded = true; } - private stopAnimation() { + private stopAnimation(): void { this.ripple.active = false; this.ripple.disabled = true; - this.style.display = "none"; + this.style.display = 'none'; } } -customElements.define("action-handler-restriction", ActionHandler); +customElements.define('action-handler-restriction', ActionHandler); const geActionHandler = (): ActionHandler => { const body = document.body; - if (body.querySelector("action-handler-restriction")) { - return body.querySelector("action-handler-restriction") as ActionHandler; + if (body.querySelector('action-handler-restriction')) { + return body.querySelector('action-handler-restriction') as ActionHandler; } - const actionhandler = document.createElement("action-handler-restriction"); + const actionhandler = document.createElement('action-handler-restriction'); body.appendChild(actionhandler); return actionhandler as ActionHandler; }; -export const actionHandlerBind = ( - element: ActionHandlerElement, - options: ActionHandlerOptions -) => { +export const actionHandlerBind = (element: ActionHandlerElement, options: ActionHandlerOptions): void => { const actionhandler: ActionHandler = geActionHandler(); if (!actionhandler) { return; @@ -198,8 +188,6 @@ export const actionHandlerBind = ( actionhandler.bind(element, options); }; -export const actionHandler = directive( - (options: ActionHandlerOptions = {}) => (part: PropertyPart) => { - actionHandlerBind(part.committer.element, options); - } -); \ No newline at end of file +export const actionHandler = directive((options: ActionHandlerOptions = {}) => (part: PropertyPart): void => { + actionHandlerBind(part.committer.element, options); +}); diff --git a/src/const.ts b/src/const.ts index 2aaf3ce..7733cfd 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1 +1 @@ -export const CARD_VERSION = '1.1.3'; \ No newline at end of file +export const CARD_VERSION = '1.1.4'; diff --git a/src/restriction-card.ts b/src/restriction-card.ts index a408e85..047153a 100644 --- a/src/restriction-card.ts +++ b/src/restriction-card.ts @@ -1,35 +1,26 @@ -import { - TemplateResult, - customElement, - LitElement, - property, - html, - CSSResult, - css, - PropertyValues -} from "lit-element"; -import { classMap } from "lit-html/directives/class-map"; +import { TemplateResult, customElement, LitElement, property, html, CSSResult, css, PropertyValues } from 'lit-element'; +import { classMap } from 'lit-html/directives/class-map'; -import { RestrictionCardConfig } from "./types"; +import { RestrictionCardConfig } from './types'; import { HomeAssistant, createThing, LovelaceCard, computeCardSize, LovelaceCardConfig, - evaluateFilter -} from "custom-card-helpers"; -import { CARD_VERSION } from "./const"; -import { actionHandler } from "./action-handler-directive"; + evaluateFilter, +} from 'custom-card-helpers'; +import { CARD_VERSION } from './const'; +import { actionHandler } from './action-handler-directive'; /* eslint no-console: 0 */ console.info( `%c RESTRICTION-CARD \n%c Version ${CARD_VERSION} `, - "color: orange; font-weight: bold; background: black", - "color: white; font-weight: bold; background: dimgray" + 'color: orange; font-weight: bold; background: black', + 'color: white; font-weight: bold; background: dimgray', ); -@customElement("restriction-card") +@customElement('restriction-card') class RestrictionCard extends LitElement implements LovelaceCard { @property() protected _config?: RestrictionCardConfig; @property() protected _hass?: HomeAssistant; @@ -37,16 +28,20 @@ class RestrictionCard extends LitElement implements LovelaceCard { set hass(hass: HomeAssistant) { this._hass = hass; - const element = this.shadowRoot!.querySelector("#card > *") as LovelaceCard; - if (element) { - element.hass = hass; + if (this.shadowRoot) { + const element = this.shadowRoot.querySelector('#card > *') as LovelaceCard; + if (element) { + element.hass = hass; + } } } public getCardSize(): number { - const element = this.shadowRoot!.querySelector("#card > *") as LovelaceCard; - if (element) { - return computeCardSize(element); + if (this.shadowRoot) { + const element = this.shadowRoot.querySelector('#card > *') as LovelaceCard; + if (element) { + return computeCardSize(element); + } } return 1; @@ -54,179 +49,156 @@ class RestrictionCard extends LitElement implements LovelaceCard { public setConfig(config: RestrictionCardConfig): void { if (!config.card) { - throw new Error("Error in card configuration."); + throw new Error('Error in card configuration.'); } - if ( - config.restrictions && - config.restrictions.pin && - !config.restrictions.pin.code - ) { - throw new Error("A pin code is required for pin restrictions"); + if (config.restrictions && config.restrictions.pin && !config.restrictions.pin.code) { + throw new Error('A pin code is required for pin restrictions'); } - this._config = { duration: 5, action: "tap", ...config }; + this._config = { duration: 5, action: 'tap', ...config }; } protected shouldUpdate(changedProps: PropertyValues): boolean { - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + const oldHass = changedProps.get('hass') as HomeAssistant | undefined; - if (changedProps.has("config") || !oldHass) { + if (changedProps.has('config') || !oldHass) { return true; } - if ( - this._config && - this._config.condition && - this._config.condition.entity - ) { - return ( - oldHass.states[this._config.condition.entity] !== - this._hass!.states[this._config.condition.entity] - ); + if (this._hass && this._config && this._config.condition && this._config.condition.entity) { + return oldHass.states[this._config.condition.entity] !== this._hass.states[this._config.condition.entity]; } else { return false; } } protected render(): TemplateResult | void { - if (!this._config || !this._hass) { + if (!this._config || !this._hass || !this._config.card) { return html``; } - if ( - this._config.restrictions && - this._matchRestriction(this._config.restrictions.hide) - ) { + if (this._config.restrictions && this._matchRestriction(this._config.restrictions.hide)) { return html``; } return html`
${(this._config.exemptions && - this._config.exemptions.some(e => e.user === this._hass!.user!.id)) || + this._config.exemptions.some(e => (this._hass && this._hass.user ? e.user === this._hass.user.id : false))) || (this._config.condition && - !evaluateFilter( - this._hass.states[this._config.condition.entity], - this._config.condition - )) - ? "" + !evaluateFilter(this._hass.states[this._config.condition.entity], this._config.condition)) + ? '' : html`
`} - ${this.renderCard(this._config.card!)} + ${this.renderCard(this._config.card)}
`; } private renderCard(config: LovelaceCardConfig): TemplateResult { - const element = createThing(config, this._config!.row); - if (this._hass) { + if (this._hass && this._config) { + const element = createThing(config, this._config.row); element.hass = this._hass; + + return html` +
+ ${element} +
+ `; } - return html` -
- ${element} -
- `; + return html``; } private _matchRestriction(restriction): boolean { return ( + this._hass && restriction && (!restriction.exemptions || - !restriction.exemptions.some(e => e.user === this._hass!.user!.id)) && - (!restriction.condition || - evaluateFilter( - this._hass!.states[restriction.condition.entity], - restriction.condition - )) + !restriction.exemptions.some(e => (this._hass && this._hass.user ? e.user === this._hass.user.id : false))) && + (!restriction.condition || evaluateFilter(this._hass.states[restriction.condition.entity], restriction.condition)) ); } private _handleAction(ev): void { - if (this._config!.action === ev.detail.action) { + if (this._config && this._config.action === ev.detail.action) { this._handleRestriction(); } } private _handleRestriction(): void { - const lock = this.shadowRoot!.getElementById("lock") as LitElement; + if (!this._config || !this.shadowRoot) { + return; + } + + const lock = this.shadowRoot.getElementById('lock') as LitElement; - if (this._config!.restrictions) { - if (this._matchRestriction(this._config!.restrictions.block)) { - if (this._config!.restrictions!.block!.text) { - alert(this._config!.restrictions!.block!.text); + if (this._config.restrictions) { + if (this._config.restrictions.block && this._matchRestriction(this._config.restrictions.block)) { + if (this._config.restrictions.block.text) { + alert(this._config.restrictions.block.text); } - lock.classList.add("invalid"); + lock.classList.add('invalid'); window.setTimeout(() => { if (lock) { - lock.classList.remove("invalid"); + lock.classList.remove('invalid'); } }, 3000); return; } - if (this._matchRestriction(this._config!.restrictions.pin)) { - const pin = prompt( - this._config!.restrictions!.pin!.text || "Input pin code" - ); + if (this._config.restrictions.pin && this._matchRestriction(this._config.restrictions.pin)) { + const pin = prompt(this._config.restrictions.pin.text || 'Input pin code'); // tslint:disable-next-line: triple-equals - if (pin != this._config!.restrictions!.pin!.code) { - lock.classList.add("invalid"); + if (pin != this._config.restrictions.pin.code) { + lock.classList.add('invalid'); window.setTimeout(() => { if (lock) { - lock.classList.remove("invalid"); + lock.classList.remove('invalid'); } }, 3000); return; } } - if (this._matchRestriction(this._config!.restrictions.confirm)) { - if ( - !confirm( - this._config!.restrictions!.confirm!.text || - "Are you sure you want to unlock?" - ) - ) { + if (this._config.restrictions.confirm && this._matchRestriction(this._config.restrictions.confirm)) { + if (!confirm(this._config.restrictions.confirm.text || 'Are you sure you want to unlock?')) { return; } } } - const overlay = this.shadowRoot!.getElementById("overlay") as LitElement; - overlay.style.setProperty("pointer-events", "none"); - lock.classList.add("hidden"); + const overlay = this.shadowRoot.getElementById('overlay') as LitElement; + overlay.style.setProperty('pointer-events', 'none'); + lock.classList.add('hidden'); window.setTimeout(() => { - overlay.style.setProperty("pointer-events", ""); + overlay.style.setProperty('pointer-events', ''); if (lock) { - lock.classList.remove("hidden"); + lock.classList.remove('hidden'); } - }, this._config!.duration! * 1000); + }, this._config.duration * 1000); } static get styles(): CSSResult { @@ -234,32 +206,14 @@ class RestrictionCard extends LitElement implements LovelaceCard { :host { display: block; position: relative; - --regular-lock-color: var( - --restriction-regular-lock-color, - var(--primary-text-color, #212121) - ); - --success-lock-color: var( - --restriction-success-lock-color, - var(--primary-color, #03a9f4) - ); - --blocked-lock-color: var( - --restriction-blocked-lock-color, - var(--error-state-color, #db4437) - ); - --invalid-lock-color: var( - --restriction-invalid--color, - var(--error-state-color, #db4437) - ); + --regular-lock-color: var(--restriction-regular-lock-color, var(--primary-text-color, #212121)); + --success-lock-color: var(--restriction-success-lock-color, var(--primary-color, #03a9f4)); + --blocked-lock-color: var(--restriction-blocked-lock-color, var(--error-state-color, #db4437)); + --invalid-lock-color: var(--restriction-invalid--color, var(--error-state-color, #db4437)); --lock-margin-left: var(--restriction-lock-margin-left, 0px); --lock-row-margin-left: var(--restriction-lock-row-margin-left, 24px); - --lock-icon-height: var( - --restriction-lock-icon-height, - var(--iron-icon-height, 24px) - ); - --lock-icon-width: var( - --restriction-lock-icon-width, - var(--iron-icon-width, 24px) - ); + --lock-icon-height: var(--restriction-lock-icon-height, var(--iron-icon-height, 24px)); + --lock-icon-width: var(--restriction-lock-icon-width, var(--iron-icon-width, 24px)); } ha-icon { --iron-icon-height: var(--lock-icon-height); @@ -308,6 +262,6 @@ class RestrictionCard extends LitElement implements LovelaceCard { declare global { interface HTMLElementTagNameMap { - "restriction-card": RestrictionCard; + 'restriction-card': RestrictionCard; } } diff --git a/src/types.ts b/src/types.ts index d17a1eb..14ede42 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { ActionConfig, LovelaceCardConfig } from "custom-card-helpers"; +import { LovelaceCardConfig } from 'custom-card-helpers'; export interface RestrictionCardConfig extends LovelaceCardConfig { restrictions?: RestrictionsConfig; @@ -51,7 +51,3 @@ export interface ConditionConfig { entity: string; attribute?: string; } - -export interface LongPressOptions { - hasDoubleClick?: boolean; -} diff --git a/tsconfig.json b/tsconfig.json index 81e948f..f95d45f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,4 +14,4 @@ "resolveJsonModule": true, "experimentalDecorators": true } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 51c7bfb..ce2bae3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -441,10 +441,10 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -custom-card-helpers@^1.3.5: - version "1.3.6" - resolved "https://registry.yarnpkg.com/custom-card-helpers/-/custom-card-helpers-1.3.6.tgz#25b70aa6d978346eb935d03b018c836c6ba62cfa" - integrity sha512-BVo1YLi9NhKd5udlS23GVNDvJ3Rl7flNkzXxXW2Llrsb2aCBR/OLGuFIfkOPMjv+bDP15GVrGM5lnkxpEXyzmA== +custom-card-helpers@^1.3.9: + version "1.4.0" + resolved "https://registry.yarnpkg.com/custom-card-helpers/-/custom-card-helpers-1.4.0.tgz#8e11f469813e699e6ee6054586b588df8707920e" + integrity sha512-IQdgHu8mt7BGbNIO5b1xVoaxIJhPXQQS4vngCuURhinaj3mWrXPVeUU832WqNdH6QqIWebTYpL98WwALij37iA== dependencies: fecha "^3.0.3" home-assistant-js-websocket "^4.4.0" @@ -549,6 +549,13 @@ eslint-config-airbnb-base@^14.0.0: object.assign "^4.1.0" object.entries "^1.1.0" +eslint-config-prettier@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.5.0.tgz#aaf9a495e2a816865e541bfdbb73a65cc162b3eb" + integrity sha512-cjXp8SbO9VFGW/Z7mbTydqS9to8Z58E5aYhj3e1+Hx7lS9s6gL5ILKNpCqZAFOVYRcSkWPFYljHrEh8QFEK5EQ== + dependencies: + get-stdin "^6.0.0" + eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -582,6 +589,13 @@ eslint-plugin-import@^2.18.2: read-pkg-up "^2.0.0" resolve "^1.11.0" +eslint-plugin-prettier@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz#507b8562410d02a03f0ddc949c616f877852f2ba" + integrity sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" @@ -709,6 +723,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -817,6 +836,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + glob-parent@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" @@ -1105,7 +1129,7 @@ lit-element@^2.1.0, lit-element@^2.2.1: dependencies: lit-html "^1.0.0" -lit-html@^1.0.0: +lit-html@^1.0.0, lit-html@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.1.2.tgz#2e3560a7075210243649c888ad738eaf0daa8374" integrity sha512-FFlUMKHKi+qG1x1iHNZ1hrtc/zHmfYTyrSvs3/wBTvaNtpZjOZGWzU7efGYVpgp6KvWeKF6ql9/KsCq6Z/mEDA== @@ -1157,6 +1181,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +mime@>=2.0.3: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -1283,6 +1312,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opener@1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" + integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== + optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -1408,6 +1442,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" @@ -1491,6 +1532,14 @@ rollup-plugin-node-resolve@^5.2.0: resolve "^1.11.1" rollup-pluginutils "^2.8.1" +rollup-plugin-serve@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-serve/-/rollup-plugin-serve-1.0.1.tgz#2da2a784a916c5564609c7696cd9dacdbf17f6cc" + integrity sha512-bni0pb4s1YLvn1xBmj+dH1OsLdp8gWA4zqh3yuEtT6/YHhg3nDneGU2GwMcRDQwY2tXzuI0uSeAlF1rY+ODitg== + dependencies: + mime ">=2.0.3" + opener "1" + rollup-plugin-terser@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.1.2.tgz#3e41256205cb75f196fc70d4634227d1002c255c"