diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..23e2bd0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: + - 'master' + paths: + - 'VERSION' + +jobs: + tag-and-release: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Git + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + + - name: Create Git Tag + id: create_tag + run: | + VERSION=$(cat VERSION) + git tag -a "v$VERSION" -m "Release $VERSION" + git push origin "v$VERSION" + + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION=$(cat VERSION) + gh release create "v$VERSION" vertical-stack-in-card.js --title "Release v$VERSION" --generate-notes diff --git a/README.md b/README.md index 7fa06bf..a7279e2 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,67 @@ # Vertical Stack In Card -![Version](https://img.shields.io/github/v/release/ofekashery/vertical-stack-in-card?style=flat-square) -![Downloads](https://img.shields.io/github/downloads/ofekashery/vertical-stack-in-card/total?style=flat-square) -![Stars](https://img.shields.io/github/stars/ofekashery/vertical-stack-in-card?style=flat-square) +![Version](https://img.shields.io/github/v/release/ofekashery/vertical-stack-in-card) +![Downloads](https://img.shields.io/github/downloads/ofekashery/vertical-stack-in-card/total) +![Stars](https://img.shields.io/github/stars/ofekashery/vertical-stack-in-card) +![HACS](https://img.shields.io/badge/HACS-Default-41BDF5.svg) -Vertical Stack In Card lets you you to group multiple cards into a single sleek card in the Home Assistant UI. +**Vertical Stack In Card** is a custom Lovelace card for Home Assistant, allowing you to group multiple cards into a single sleek card. It offers a clean, organized way to display multiple cards in your Home Assistant dashboard. -![Example Card](https://user-images.githubusercontent.com/16443111/220773923-c28009d6-edfc-4ffd-9290-3e0c6e1acf73.png) +![Showcase Card](https://user-images.githubusercontent.com/16443111/220773923-c28009d6-edfc-4ffd-9290-3e0c6e1acf73.png) -## Options +## Configuration Options -| Name | Type | Default | Description | -| ---------- | ------- | ------------ | ----------------------------------------- | -| type | string | **Required** | `custom:vertical-stack-in-card` | -| cards | list | **Required** | List of cards | -| title | string | **Optional** | Card title | -| horizontal | boolean | **Optional** | Default: `false` | -| styles | object | **Optional** | Adds custom CSS directives to child cards | +| Name | Type | Default | Description | +| ------------ | ------- | ------- | ------------------------------------------------- | +| `type` | string | N/A | Must be `custom:vertical-stack-in-card`. | +| `cards` | list | N/A | List of cards to include. | +| `title` | string | None | Optional. Title displayed at the top of the card. | +| `horizontal` | boolean | false | Optional. Whatever stack cards horizontally. | +| `styles` | object | None | Optional. Add custom CSS for advanced styling. | ## Installation -*VSIC is available in [HACS](https://github.com/hacs/integration) (Home Assistant Community Store).* +### Via HACS (Home Assistant Community Store) -### 1. Download the card +1. Open HACS in Home Assistant. +2. Search for "Vertical Stack In Card." +3. Install and follow the setup instructions. + +### Manual Installation + +Download the [`vertical-stack-in-card.js`](https://raw.githubusercontent.com/ofekashery/vertical-stack-in-card/master/vertical-stack-in-card.js) into your `/www` directory. -Install the VSIC by copying [`vertical-stack-in-card.js`](https://raw.githubusercontent.com/ofekashery/vertical-stack-in-card/master/vertical-stack-in-card.js) to `/www/vertical-stack-in-card.js`, or via bash: ```bash wget https://raw.githubusercontent.com/ofekashery/vertical-stack-in-card/master/vertical-stack-in-card.js mv vertical-stack-in-card.js /config/www/ ``` -### 2. Link the card to your Lovelace UI +#### Add resource reference -#### The manual way: - -Link `vertical-stack-in-card` inside your `ui-lovelace.yaml` +If you configure Lovelace via YAML, add a reference to `vertical-stack-in-card.js` inside your `configuration.yaml`: ```yaml resources: - - url: /local/vertical-stack-in-card.js?v=0.4.4 + - url: /local/vertical-stack-in-card.js?v=1.0.1 type: js ``` -#### Through the GUI: +Alternatively, if you prefer the graphical editor, use the menu to add the resource. + +1. Make sure, **advanced mode** is enabled in your user profile (click on your user name to get there). -Alternatively, with Home Assistant 2021.3 or later, [click here](https://my.home-assistant.io/redirect/lovelace_resources). Using My Home Assistant, that will bring up the GUI for Resources. Click the Plus to add a new resource. The `url` is the path to your downloaded file. Replace `/www/` with `/local/`. +2. Navigate to the **Configuration** -> **Lovelace Dashboards** -> **Resources**. -![Add Resource](https://user-images.githubusercontent.com/557102/196027109-01b3ab95-ef61-4573-9ced-71233481eb07.png) +3. Click on **Add resource**, and fill out the form as follows: -Finish by clicking "Create" and refresh your browser. + - **Url:** `/local/vertical-stack-in-card.js?v=1.0.1` + - **Resource type:** `JavaScript Module` -### 3. Use the card somehere. +4. Finish by clicking **Create** and refresh your browser. -Add a custom card in your `ui-lovelace.yaml`. +## Usage -**Example** +Add the card to your Lovelace UI configuration: ```yaml type: 'custom:vertical-stack-in-card' @@ -68,13 +75,10 @@ cards: - type: entities entities: - switch.livingroom_tv - - entity: script.livingroom_receiver - name: Receiver - switch.livingroom_ac + - light.ambient_lights ``` -## Credits +## Acknowledgements -- [ofekashery](https://github.com/ofekashery) -- [ciotlosm](https://github.com/ciotlosm) -- [thomasloven](https://github.com/thomasloven) +Thanks to [@ciotlosm](https://github.com/ciotlosm) and [@thomasloven](https://github.com/thomasloven) for their inspiration and contributions in building the foundation of this project. diff --git a/VERSION b/VERSION index 8f0916f..7dea76e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0 +1.0.1 diff --git a/vertical-stack-in-card.js b/vertical-stack-in-card.js index d809197..f4b18bf 100644 --- a/vertical-stack-in-card.js +++ b/vertical-stack-in-card.js @@ -1,4 +1,8 @@ -console.log(`%cvertical-stack-in-card\n%cVersion: ${'0.4.4'}`, 'color: #1976d2; font-weight: bold;', ''); +console.log( + `%cvertical-stack-in-card\n%cVersion: ${'1.0.1'}`, + 'color: #1976d2; font-weight: bold;', + '' +); class VerticalStackInCard extends HTMLElement { constructor() { @@ -7,7 +11,9 @@ class VerticalStackInCard extends HTMLElement { setConfig(config) { this._cardSize = {}; - this._cardSize.promise = new Promise((resolve) => (this._cardSize.resolve = resolve)); + this._cardSize.promise = new Promise( + (resolve) => (this._cardSize.resolve = resolve) + ); if (!config || !config.cards || !Array.isArray(config.cards)) { throw new Error('Card config incorrect'); @@ -19,18 +25,17 @@ class VerticalStackInCard extends HTMLElement { async renderCard() { const config = this._config; - if (window.loadCardHelpers) { - this.helpers = await window.loadCardHelpers(); - } - const promises = config.cards.map((config) => this.createCardElement(config)); + 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)); + card.updateComplete.then(() => this._styleCard(card)); } else { - this.styleCard(card); + this._styleCard(card); } }); @@ -48,8 +53,8 @@ class VerticalStackInCard extends HTMLElement { }); } card.appendChild(cardContent); - - const shadowRoot = this.shadowRoot || this.attachShadow({mode: 'open'}); + + const shadowRoot = this.shadowRoot || this.attachShadow({ mode: 'open' }); while (shadowRoot.hasChildNodes()) { shadowRoot.removeChild(shadowRoot.lastChild); } @@ -59,50 +64,19 @@ class VerticalStackInCard extends HTMLElement { 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; - }; + async _createCardElement(cardConfig) { + const helpers = await window.loadCardHelpers(); + const element = + cardConfig.type === 'divider' + ? helpers.createRowElement(cardConfig) + : helpers.createCardElement(cardConfig); - 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._createCardElement(cardConfig).then(() => { this.renderCard(); }); }, @@ -120,16 +94,18 @@ class VerticalStackInCard extends HTMLElement { } } - styleCard(element) { + _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"; + ele.style.border = 'none'; if ('styles' in config) { - Object.entries(config.styles).forEach(([key, value]) => ele.style.setProperty(key, value)); + Object.entries(config.styles).forEach(([key, value]) => + ele.style.setProperty(key, value) + ); } } else { let searchEles = element.shadowRoot.getElementById('root'); @@ -142,17 +118,22 @@ class VerticalStackInCard extends HTMLElement { if (searchEles[i].style) { searchEles[i].style.margin = '0px'; } - this.styleCard(searchEles[i]); + this._styleCard(searchEles[i]); } } } else { - if (typeof element.querySelector === 'function' && element.querySelector('ha-card')) { + 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"; + ele.style.border = 'none'; if ('styles' in config) { - Object.entries(config.styles).forEach(([key, value]) => ele.style.setProperty(key, value)); + Object.entries(config.styles).forEach(([key, value]) => + ele.style.setProperty(key, value) + ); } } let searchEles = element.childNodes; @@ -160,7 +141,7 @@ class VerticalStackInCard extends HTMLElement { if (searchEles[i] && searchEles[i].style) { searchEles[i].style.margin = '0px'; } - this.styleCard(searchEles[i]); + this._styleCard(searchEles[i]); } } } @@ -181,8 +162,27 @@ class VerticalStackInCard extends HTMLElement { return sizes.reduce((a, b) => a + b, 0); } - static getConfigElement() { - return customElements.get('hui-vertical-stack-card').getConfigElement(); + static async getConfigElement() { + // Ensure the hui-stack-card-editor is loaded. + let cls = customElements.get('hui-vertical-stack-card'); + if (!cls) { + const helpers = await window.loadCardHelpers(); + helpers.createCardElement({ type: 'vertical-stack', cards: [] }); + await customElements.whenDefined('hui-vertical-stack-card'); + cls = customElements.get('hui-vertical-stack-card'); + } + const configElement = await cls.getConfigElement(); + + // Patch setConfig to remove non-VSIC config options. + const originalSetConfig = configElement.setConfig; + configElement.setConfig = (config) => + originalSetConfig.call(configElement, { + type: config.type, + title: config.title, + cards: config.cards || [], + }); + + return configElement; } static getStubConfig() { @@ -199,4 +199,5 @@ window.customCards.push({ name: 'Vertical Stack In Card', description: 'Group multiple cards into a single sleek card.', preview: false, -}); \ No newline at end of file + documentationURL: 'https://github.com/ofekashery/vertical-stack-in-card', +});