190 lines
5.4 KiB
JavaScript
190 lines
5.4 KiB
JavaScript
|
console.log(`%cvertical-stack-in-card\n%cVersion: ${'0.4.4'}`, 'color: #1976d2; font-weight: bold;', '');
|
||
|
|
||
|
class VerticalStackInCard extends HTMLElement {
|
||
|
constructor() {
|
||
|
super();
|
||
|
}
|
||
|
|
||
|
setConfig(config) {
|
||
|
this._cardSize = {};
|
||
|
this._cardSize.promise = new Promise((resolve) => (this._cardSize.resolve = resolve));
|
||
|
|
||
|
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||
|
throw new Error('Card config incorrect');
|
||
|
}
|
||
|
this._config = config;
|
||
|
this._refCards = [];
|
||
|
this.renderCard();
|
||
|
}
|
||
|
|
||
|
async renderCard() {
|
||
|
const config = this._config;
|
||
|
if (window.loadCardHelpers) {
|
||
|
this.helpers = await window.loadCardHelpers();
|
||
|
}
|
||
|
const promises = config.cards.map((config) => this.createCardElement(config));
|
||
|
this._refCards = await Promise.all(promises);
|
||
|
|
||
|
// Style cards
|
||
|
this._refCards.forEach((card) => {
|
||
|
if (card.updateComplete) {
|
||
|
card.updateComplete.then(() => this.styleCard(card));
|
||
|
} else {
|
||
|
this.styleCard(card);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Create the card
|
||
|
const card = document.createElement('ha-card');
|
||
|
const cardContent = document.createElement('div');
|
||
|
card.header = config.title;
|
||
|
card.style.overflow = 'hidden';
|
||
|
this._refCards.forEach((card) => cardContent.appendChild(card));
|
||
|
if (config.horizontal) {
|
||
|
cardContent.style.display = 'flex';
|
||
|
cardContent.childNodes.forEach((card) => {
|
||
|
card.style.flex = '1 1 0';
|
||
|
card.style.minWidth = 0;
|
||
|
});
|
||
|
}
|
||
|
card.appendChild(cardContent);
|
||
|
|
||
|
const shadowRoot = this.shadowRoot || this.attachShadow({mode: 'open'});
|
||
|
while (shadowRoot.hasChildNodes()) {
|
||
|
shadowRoot.removeChild(shadowRoot.lastChild);
|
||
|
}
|
||
|
shadowRoot.appendChild(card);
|
||
|
|
||
|
// Calculate card size
|
||
|
this._cardSize.resolve();
|
||
|
}
|
||
|
|
||
|
async createCardElement(cardConfig) {
|
||
|
const createError = (error, origConfig) => {
|
||
|
return createThing('hui-error-card', {
|
||
|
type: 'error',
|
||
|
error,
|
||
|
origConfig
|
||
|
});
|
||
|
};
|
||
|
|
||
|
const createThing = (tag, config) => {
|
||
|
if (this.helpers) {
|
||
|
if (config.type === 'divider') {
|
||
|
return this.helpers.createRowElement(config);
|
||
|
} else {
|
||
|
return this.helpers.createCardElement(config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const element = document.createElement(tag);
|
||
|
try {
|
||
|
element.setConfig(config);
|
||
|
} catch (err) {
|
||
|
console.error(tag, err);
|
||
|
return createError(err.message, config);
|
||
|
}
|
||
|
return element;
|
||
|
};
|
||
|
|
||
|
let tag = cardConfig.type;
|
||
|
if (tag.startsWith('divider')) {
|
||
|
tag = `hui-divider-row`;
|
||
|
} else if (tag.startsWith('custom:')) {
|
||
|
tag = tag.substr('custom:'.length);
|
||
|
} else {
|
||
|
tag = `hui-${tag}-card`;
|
||
|
}
|
||
|
|
||
|
const element = createThing(tag, cardConfig);
|
||
|
element.hass = this._hass;
|
||
|
element.addEventListener(
|
||
|
'll-rebuild',
|
||
|
(ev) => {
|
||
|
ev.stopPropagation();
|
||
|
this.createCardElement(cardConfig).then(() => {
|
||
|
this.renderCard();
|
||
|
});
|
||
|
},
|
||
|
{ once: true }
|
||
|
);
|
||
|
return element;
|
||
|
}
|
||
|
|
||
|
set hass(hass) {
|
||
|
this._hass = hass;
|
||
|
if (this._refCards) {
|
||
|
this._refCards.forEach((card) => {
|
||
|
card.hass = hass;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
styleCard(element) {
|
||
|
const config = this._config;
|
||
|
if (element.shadowRoot) {
|
||
|
if (element.shadowRoot.querySelector('ha-card')) {
|
||
|
let ele = element.shadowRoot.querySelector('ha-card');
|
||
|
ele.style.boxShadow = 'none';
|
||
|
ele.style.borderRadius = '0';
|
||
|
ele.style.border = "none";
|
||
|
if ('styles' in config) {
|
||
|
Object.entries(config.styles).forEach(([key, value]) => ele.style.setProperty(key, value));
|
||
|
}
|
||
|
} else {
|
||
|
let searchEles = element.shadowRoot.getElementById('root');
|
||
|
if (!searchEles) {
|
||
|
searchEles = element.shadowRoot.getElementById('card');
|
||
|
}
|
||
|
if (!searchEles) return;
|
||
|
searchEles = searchEles.childNodes;
|
||
|
for (let i = 0; i < searchEles.length; i++) {
|
||
|
if (searchEles[i].style) {
|
||
|
searchEles[i].style.margin = '0px';
|
||
|
}
|
||
|
this.styleCard(searchEles[i]);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (typeof element.querySelector === 'function' && element.querySelector('ha-card')) {
|
||
|
let ele = element.querySelector('ha-card');
|
||
|
ele.style.boxShadow = 'none';
|
||
|
ele.style.borderRadius = '0';
|
||
|
ele.style.border = "none";
|
||
|
if ('styles' in config) {
|
||
|
Object.entries(config.styles).forEach(([key, value]) => ele.style.setProperty(key, value));
|
||
|
}
|
||
|
}
|
||
|
let searchEles = element.childNodes;
|
||
|
for (let i = 0; i < searchEles.length; i++) {
|
||
|
if (searchEles[i] && searchEles[i].style) {
|
||
|
searchEles[i].style.margin = '0px';
|
||
|
}
|
||
|
this.styleCard(searchEles[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_computeCardSize(card) {
|
||
|
if (typeof card.getCardSize === 'function') {
|
||
|
return card.getCardSize();
|
||
|
}
|
||
|
return customElements
|
||
|
.whenDefined(card.localName)
|
||
|
.then(() => this._computeCardSize(card))
|
||
|
.catch(() => 1);
|
||
|
}
|
||
|
|
||
|
async getCardSize() {
|
||
|
await this._cardSize.promise;
|
||
|
const sizes = await Promise.all(this._refCards.map(this._computeCardSize));
|
||
|
return sizes.reduce((a, b) => a + b, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
customElements.define('vertical-stack-in-card', VerticalStackInCard);
|
||
|
|
||
|
window.customElements.get('vertical-stack-in-card').getConfigElement = function() {
|
||
|
return document.createElement('hui-stack-card-editor');
|
||
|
}
|