Initial commit
This commit is contained in:
commit
3e49d0737f
169
README.md
Normal file
169
README.md
Normal file
@ -0,0 +1,169 @@
|
||||
layout-card
|
||||
===========
|
||||
|
||||
Get more control over the placement of cards
|
||||
|
||||
> You may think of this as a test-drive for the future of default lovelace behavior. As such I appreciate any feedback (even more than usual). Let me know if and how you use this, and we might see it as default behavior in the future. Or something else entirely, who knows...
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Type | Default | Description
|
||||
| ---- | ---- | ------- | -----------
|
||||
| type | string | **Required** | `custom:layout-card`
|
||||
| cards | list | **Required** | The cards to display
|
||||
| layout | string | auto | `auto`, `vertical`, `horizontal`
|
||||
| column\_num | number | none | Number of columns to force
|
||||
|
||||
## Instructions
|
||||
|
||||
This card takes other cards and place them in different layouts.
|
||||
|
||||
The card works best if used in a view with `panel: true`.
|
||||
|
||||
Each layout consists of columns, the number of which is determined by your screen width. The number of columns can also be overridden by setting `column_num`.
|
||||
|
||||
There are three different layouts:
|
||||
|
||||
### `auto`
|
||||
|
||||
This layout works the same way as the default lovelace layout. Cards are automatically added to stacks depending on their height.
|
||||
That may seem pointless, but trust me, this has it's uses.
|
||||
|
||||

|
||||
```yaml
|
||||
- title: Default
|
||||
panel: true
|
||||
cards:
|
||||
- type: custom:layout-card
|
||||
cards:
|
||||
- type: entities
|
||||
title: 1
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 2
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 3
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 4
|
||||
entities:
|
||||
- light.bed_light
|
||||
- light.bed_light
|
||||
- light.bed_light
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 5
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 6
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 7
|
||||
entities:
|
||||
- light.bed_light
|
||||
- type: entities
|
||||
title: 8
|
||||
entities:
|
||||
- light.bed_light
|
||||
```
|
||||
|
||||
### horizontal
|
||||
|
||||
This layout will place cards in one column at a time, and then move on to the next - horizontally.
|
||||
|
||||

|
||||
```yaml
|
||||
- title: Horizontal
|
||||
panel: true
|
||||
cards:
|
||||
- type: custom:layout-card
|
||||
layout: horizontal
|
||||
cards:
|
||||
- type: entities
|
||||
```
|
||||
|
||||
### vertical
|
||||
|
||||
This layout will place cards vertically in one column
|
||||
|
||||

|
||||
```yaml
|
||||
- title: Vertical
|
||||
panel: true
|
||||
cards:
|
||||
- type: custom:layout-card
|
||||
layout: vertical
|
||||
cards:
|
||||
- type: entities
|
||||
```
|
||||
|
||||
It's OK to think I'm out of my mind at this point, but hang on; let me introduce the `break`.
|
||||
|
||||
Add a `- break` to the list of cards to break the column right there, and move on to the next one.
|
||||
|
||||

|
||||
```yaml
|
||||
- title: Manual breaks
|
||||
panel: true
|
||||
cards:
|
||||
- type: custom:layout-card
|
||||
layout: vertical
|
||||
cards:
|
||||
- type: entities
|
||||
title: 1
|
||||
...
|
||||
- type: entities
|
||||
title: 2
|
||||
...
|
||||
- break
|
||||
- type: entities
|
||||
title: 3
|
||||
...
|
||||
- break
|
||||
- type: entities
|
||||
title: 4
|
||||
...
|
||||
- type: entities
|
||||
title: 5
|
||||
...
|
||||
- type: entities
|
||||
title: 6
|
||||
...
|
||||
- break
|
||||
- type: entities
|
||||
title: 7
|
||||
...
|
||||
- type: entities
|
||||
title: 8
|
||||
...
|
||||
```
|
||||
|
||||
Breaks also work with the `horizontal` layout, and even `auto`. Experiment a bit.
|
||||
|
||||
---
|
||||
|
||||
And as mentioned, use `column_num` to override the number of columns.
|
||||
|
||||

|
||||
```yaml
|
||||
- type: custom:layout-card
|
||||
layout: auto
|
||||
column_num: 2
|
||||
cards:
|
||||
...
|
||||
```
|
||||
|
||||

|
||||
```yaml
|
||||
- type: custom:layout-card
|
||||
layout: horizontal
|
||||
column_num: 8
|
||||
cards:
|
||||
...
|
||||
```
|
156
layout-card.js
Normal file
156
layout-card.js
Normal file
@ -0,0 +1,156 @@
|
||||
class LayoutCard extends Polymer.Element {
|
||||
|
||||
static get template() {
|
||||
return Polymer.html`
|
||||
<style>
|
||||
#columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.column {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 500px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.column > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.column > * {
|
||||
display: block;
|
||||
margin: 4px 4px 8px;
|
||||
}
|
||||
</style>
|
||||
<div id="columns"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
makeCard(config) {
|
||||
let tag = config.type;
|
||||
if(tag.startsWith("custom:"))
|
||||
tag = tag.substr(7);
|
||||
else
|
||||
tag = `hui-${tag}-card`;
|
||||
let card = document.createElement(tag);
|
||||
card.setConfig(config);
|
||||
return card;
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
this.config = config;
|
||||
|
||||
this.colnum = 0;
|
||||
this.config.min_height = this.config.minheight || 5;
|
||||
this.config.column_width = this.config.column_width || 300;
|
||||
this.config.layout = this.config.layout || 'auto';
|
||||
|
||||
this._cards = config.cards.map((c) => {
|
||||
if (typeof c === 'string') return c;
|
||||
let el = this.makeCard(c);
|
||||
if (this.hass) el.hass = this.hass;
|
||||
return el;
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => this._updateColumns());
|
||||
window.setTimeout(() => this._updateColumns(), 10);
|
||||
}
|
||||
|
||||
_updateColumns() {
|
||||
let numcols = 0;
|
||||
if (this.config.column_num) {
|
||||
numcols = this.config.column_num;
|
||||
} else {
|
||||
let colWidth = this.config.column_width || 300;
|
||||
numcols = Math.max(1,
|
||||
Math.floor(this.$.columns.clientWidth/this.config.column_width));
|
||||
}
|
||||
if(numcols != this.colnum) {
|
||||
this.colnum = numcols;
|
||||
this._build();
|
||||
}
|
||||
}
|
||||
|
||||
_build() {
|
||||
const root = this.$.columns;
|
||||
while(root.lastChild) root.removeChild(root.lastChild);
|
||||
|
||||
let cols = [];
|
||||
let colLen = [];
|
||||
for(let i = 0; i < this.colnum; i++) {
|
||||
cols.push([]);
|
||||
colLen.push(0);
|
||||
}
|
||||
|
||||
let shortest = () => {
|
||||
let i = 0;
|
||||
for(let j = 0; j < colLen.length; j++) {
|
||||
if(colLen[j] < this.config.min_height) {
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
if(colLen[j] < colLen[i])
|
||||
i = j;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
this._cards.forEach((c) => {
|
||||
let sz;
|
||||
if(typeof c !== 'string') {
|
||||
this.appendChild(c);
|
||||
sz = typeof c.getCardSize === 'function' ? c.getCardSize() : 1;
|
||||
}
|
||||
switch (this.config.layout) {
|
||||
case 'auto':
|
||||
if(typeof c === 'string') break;
|
||||
cols[shortest()].push(c);
|
||||
colLen[shortest()] += sz;
|
||||
break;
|
||||
case 'vertical':
|
||||
if(typeof c === 'string') {
|
||||
i += 1;
|
||||
} else {
|
||||
if (i >= this.colnum) i = 0;
|
||||
cols[i].push(c);
|
||||
colLen[i] += sz;
|
||||
}
|
||||
break;
|
||||
case 'horizontal':
|
||||
if(c instanceof String) {
|
||||
i += 1;
|
||||
} else {
|
||||
if (i >= this.colnum) i = 0;
|
||||
cols[i].push(c);
|
||||
colLen[i] += sz;
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
cols = cols.filter(c => c.length > 0);
|
||||
let maxlen = 0;
|
||||
cols.forEach( c => {
|
||||
const cEl = document.createElement('div');
|
||||
cEl.classList.add('column');
|
||||
c.filter(e => typeof e !== 'string').forEach(e => cEl.appendChild(e));
|
||||
root.appendChild(cEl);
|
||||
if(c.length > maxlen) maxlen = c.length;
|
||||
});
|
||||
|
||||
this.maxlen = maxlen;
|
||||
}
|
||||
|
||||
set hass(hass) {
|
||||
this._cards.filter(c => typeof c !== 'string').forEach(c => c.hass = hass);
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
return this.maxlen;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('layout-card', LayoutCard);
|
Loading…
x
Reference in New Issue
Block a user