diff --git a/README.md b/README.md index 2d1ed84..59654b3 100644 --- a/README.md +++ b/README.md @@ -316,5 +316,8 @@ resources: type: js ``` +## Known issues +If you use elements_pos then it is inserted dynamically in to the DOM and so it is shown above other elements. Normally should not be an issue. + ## License This project is under the Apache 2.0 license. diff --git a/dist/group-element-bundle.js b/dist/group-element-bundle.js index cd8d7a7..ffb783d 100644 --- a/dist/group-element-bundle.js +++ b/dist/group-element-bundle.js @@ -1,123 +1,116 @@ -; - -(function () { - "use strict"; - - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : factory(); - })(this, function () { - 'use strict'; - - function deepcopy(value) { - if (!(!!value && typeof value == 'object')) { - return value; - } - - if (Object.prototype.toString.call(value) == '[object Date]') { - return new Date(value.getTime()); - } - - if (Array.isArray(value)) { - return value.map(deepcopy); - } - - var result = {}; - Object.keys(value).forEach(function (key) { - result[key] = deepcopy(value[key]); - }); - return result; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + function deepcopy(value) { + if (!(!!value && typeof value == 'object')) { + return value; } - - const fireEvent = (node, type, detail) => { - detail = detail === null || detail === undefined ? {} : detail; - const event = new Event(type, { - bubbles: true, - cancelable: false, - composed: true - }); - event.detail = detail; - node.dispatchEvent(event); - return event; - }; - - const CUSTOM_TYPE_PREFIX = "custom:"; - const TIMEOUT = 2000; - - const createHuiElement = (config, eventTarget) => { - if (!config || typeof config !== "object" || !config.type) { - return _createErrorElement("No element type configured.", config); - } - - if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) { - const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length); - - if (customElements.get(tag)) { - return _createElement(tag, config); - } - - const element = _createErrorElement("Custom element doesn't exist: ".concat(tag, "."), config); - - const timer = _hideErrorElement(element); - - customElements.whenDefined(tag).then(() => { - clearTimeout(timer); - - if (eventTarget) { - fireEvent(eventTarget, "ll-rebuild"); - } - }); - return element; - } - - return _createElement("hui-".concat(config.type, "-element"), config); - }; - - const _createElement = (tag, config) => { - const element = document.createElement(tag); - - try { - element.setConfig(deepcopy(config)); - } catch (err) { - return _createErrorElement(err.message, config); - } - - return element; - }; - - const _createErrorElement = (error, config) => { - const el = document.createElement("hui-error-card"); - el.setConfig({ - type: "error", - error, - config - }); - return el; - }; - - function _hideErrorElement(element) { - element.style.display = "None"; - return window.setTimeout(() => { - element.style.display = ""; - }, TIMEOUT); + if (Object.prototype.toString.call(value) == '[object Date]') { + return new Date(value.getTime()); } - - function createStyledHuiElement(elementConfig, eventTarget) { - const element = createHuiElement(elementConfig, eventTarget); - - if (element.tagName !== "HUI-CONDITIONAL-ELEMENT") { - element.classList.add("element"); - } - - if (elementConfig.style) { - Object.keys(elementConfig.style).forEach(prop => { - element.style.setProperty(prop, elementConfig.style[prop]); - }); - } - - return element; + if (Array.isArray(value)) { + return value.map(deepcopy); } - - customElements.define("group-element", class HuiGroupElement extends HTMLElement { + var result = {}; + Object.keys(value).forEach( + function(key) { result[key] = deepcopy(value[key]); }); + return result; + } + + const fireEvent = (node, type, detail) => { + detail = detail === null || detail === undefined ? {} : detail; + const event = new Event(type, { + bubbles: true, + cancelable: false, + composed: true, + }); + event.detail = detail; + node.dispatchEvent(event); + return event; + }; + + const CUSTOM_TYPE_PREFIX = "custom:"; + const TIMEOUT = 2000; + + const createHuiElement = (config, eventTarget) => { + if (!config || typeof config !== "object" || !config.type) { + return _createErrorElement("No element type configured.", config); + } + + if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) { + const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length); + + if (customElements.get(tag)) { + return _createElement(tag, config); + } + const element = _createErrorElement( + `Custom element doesn't exist: ${tag}.`, + config + ); + const timer = _hideErrorElement(element); + + customElements.whenDefined(tag).then(() => { + clearTimeout(timer); + if (eventTarget) { + fireEvent(eventTarget, "ll-rebuild"); + } + }); + + return element; + } + + return _createElement(`hui-${config.type}-element`, config); + }; + + const _createElement = (tag, config) => { + const element = document.createElement(tag); + try { + element.setConfig(deepcopy(config)); + } catch (err) { + return _createErrorElement(err.message, config); + } + return element; + }; + + const _createErrorElement = (error, config) => { + const el = document.createElement("hui-error-card"); + el.setConfig({ + type: "error", + error, + config, + }); + return el; + }; + + function _hideErrorElement(element) { + element.style.display = "None"; + return window.setTimeout(() => { + element.style.display = ""; + }, TIMEOUT); + } + + function createStyledHuiElement(elementConfig, eventTarget) { + const element = createHuiElement(elementConfig, eventTarget); + // keep conditional card as a transparent container so let its position remain static + if (element.tagName !== "HUI-CONDITIONAL-ELEMENT") { + element.classList.add("element"); + } + + if (elementConfig.style) { + Object.keys(elementConfig.style).forEach((prop) => { + element.style.setProperty(prop, elementConfig.style[prop]); + }); + } + + return element; + } + + customElements.define( + "group-element", + class HuiGroupElement extends HTMLElement { constructor() { super(); this._hass = {}; @@ -127,12 +120,12 @@ this._toggleTap = false; this._container = undefined; this._groupingCode = -1; - this.addEventListener("click", ev => { + + this.addEventListener("click", (ev) => { if (ev.target !== this) { ev.stopPropagation(); return; } - this.toggleVisibility(); }); } @@ -143,7 +136,7 @@ } if (this._elements.length > 0) { - this._elements.map(el => { + this._elements.map((el) => { if (el.parentElement) { el.parentElement.removeChild(el); } @@ -153,8 +146,8 @@ } this._config = config; - this.style.transform = "none"; + this.style.transform = "none"; if (config.visible !== undefined) { this._visible = config.visible; } @@ -173,9 +166,13 @@ set hass(hass) { this._hass = hass; + this.updateElements(); } + // required since otherwise if child custom elements are loaded later even fired (ll-rebuild) will not be handled + // because this element does not yet have a parent. If this is ever added as a non custom element remove this + remove also: || !this.parentElement) { + // from updateElements connectedCallback() { this.updateElements(); } @@ -190,18 +187,28 @@ } if (this._elements.length === 0) { - this._config.elements.map(elementConfig => { - const element = createStyledHuiElement(elementConfig, this.parentElement); + this._config.elements.map((elementConfig) => { + const element = createStyledHuiElement( + elementConfig, + this.parentElement + ); this._elements.push(element); }); if (this._config.elements_pos !== undefined) { - this._container = this.createElementsContainer(this._config.elements_pos); + this._container = this.createElementsContainer( + this._config.elements_pos + ); } - if (this._config.close_button !== undefined && this._config.close_button.show) { - this._elements.push(this.createCloseButton(this._config.close_button)); + if ( + this._config.close_button !== undefined && + this._config.close_button.show + ) { + this._elements.push( + this.createCloseButton(this._config.close_button) + ); } } @@ -210,6 +217,7 @@ if (container !== this) { if (this._visible) { if (!this._container.parentElement) { + // append the element container to the group's parent for same level positioning this.parentElement.appendChild(this._container); } } else if (this._container.parentElement) { @@ -217,10 +225,9 @@ } } - this._elements.map(el => { + this._elements.map((el) => { if (this._visible) { el.hass = this._hass; - if (!el.parentElement) { container.appendChild(el); } @@ -239,7 +246,7 @@ this._container = undefined; } - this._elements.map(el => { + this._elements.map((el) => { if (el.parentElement) { el.parentElement.removeChild(el); } @@ -254,11 +261,16 @@ } this._visible = !this._visible; + this.updateElements(); if (this._visible && this._groupingCode !== -1) { - this.parentElement.querySelectorAll("group-element").forEach(el => { - if (el !== this && el.attributes.groupingCode !== undefined && el.attributes.groupingCode === this._groupingCode) { + this.parentElement.querySelectorAll("group-element").forEach((el) => { + if ( + el !== this && + el.attributes.groupingCode !== undefined && + el.attributes.groupingCode === this._groupingCode + ) { if (el._visible) { el.toggleVisibility(); } @@ -269,26 +281,33 @@ createElementsContainer(elementsPosConfig) { const element = document.createElement("div"); + element.className = "element"; - Object.keys(elementsPosConfig).forEach(prop => { + + Object.keys(elementsPosConfig).forEach((prop) => { element.style.setProperty(prop, elementsPosConfig[prop]); }); + return element; } createCloseButton(buttonConfig) { const element = document.createElement("ha-icon"); + element.group = this; - element.icon = buttonConfig.icon !== undefined ? buttonConfig.icon : "hass:close"; + + element.icon = + buttonConfig.icon !== undefined ? buttonConfig.icon : "hass:close"; + element.className = "element"; if (buttonConfig.style) { - Object.keys(buttonConfig.style).forEach(prop => { + Object.keys(buttonConfig.style).forEach((prop) => { element.style.setProperty(prop, buttonConfig.style[prop]); }); } - element.addEventListener("click", ev => { + element.addEventListener("click", (ev) => { if (ev.target !== element) { ev.stopPropagation(); return; @@ -298,9 +317,10 @@ element.group.toggleVisibility(); } }); + return element; } + } + ); - }); - }); -})(); +})));