diff --git a/src/components/ha-grid-size-picker.ts b/src/components/ha-grid-size-picker.ts
index dca2cbd2fbe1..453a24cb8d30 100644
--- a/src/components/ha-grid-size-picker.ts
+++ b/src/components/ha-grid-size-picker.ts
@@ -7,6 +7,7 @@ import { mdiRestore } from "@mdi/js";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import { HomeAssistant } from "../types";
+import { conditionalClamp } from "../common/number/clamp";
type GridSizeValue = {
rows?: number;
@@ -42,6 +43,10 @@ export class HaGridSizeEditor extends LitElement {
}
protected render() {
+ const disabledColumns =
+ this.columnMin !== undefined && this.columnMin === this.columnMax;
+ const disabledRows =
+ this.rowMin !== undefined && this.rowMin === this.rowMax;
return html`
${!this.isDefault
? html`
@@ -100,17 +107,11 @@ export class HaGridSizeEditor extends LitElement {
.map((_, index) => {
const row = Math.floor(index / this.columns) + 1;
const column = (index % this.columns) + 1;
- const disabled =
- (this.rowMin !== undefined && row < this.rowMin) ||
- (this.rowMax !== undefined && row > this.rowMax) ||
- (this.columnMin !== undefined && column < this.columnMin) ||
- (this.columnMax !== undefined && column > this.columnMax);
return html`
`;
@@ -126,11 +127,16 @@ export class HaGridSizeEditor extends LitElement {
_cellClick(ev) {
const cell = ev.currentTarget as HTMLElement;
- if (cell.getAttribute("disabled") !== null) return;
const rows = Number(cell.getAttribute("data-row"));
const columns = Number(cell.getAttribute("data-column"));
+ const clampedRow = conditionalClamp(rows, this.rowMin, this.rowMax);
+ const clampedColumn = conditionalClamp(
+ columns,
+ this.columnMin,
+ this.columnMax
+ );
fireEvent(this, "value-changed", {
- value: { rows, columns },
+ value: { rows: clampedRow, columns: clampedColumn },
});
}
diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts
index 160e5c427747..f7492d0f65a1 100644
--- a/src/panels/lovelace/cards/hui-button-card.ts
+++ b/src/panels/lovelace/cards/hui-button-card.ts
@@ -145,9 +145,16 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
this._config?.show_icon &&
(this._config?.show_name || this._config?.show_state)
) {
- return { grid_rows: 2, grid_columns: 2 };
+ return {
+ grid_rows: 2,
+ grid_columns: 2,
+ grid_min_rows: 2,
+ };
}
- return { grid_rows: 1, grid_columns: 1 };
+ return {
+ grid_rows: 1,
+ grid_columns: 1,
+ };
}
public setConfig(config: ButtonCardConfig): void {
diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts
index e6366cd28b6c..42861e70fe4e 100644
--- a/src/panels/lovelace/cards/hui-entity-card.ts
+++ b/src/panels/lovelace/cards/hui-entity-card.ts
@@ -36,7 +36,11 @@ import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { createHeaderFooterElement } from "../create-element/create-header-footer-element";
-import { LovelaceCard, LovelaceHeaderFooter } from "../types";
+import {
+ LovelaceCard,
+ LovelaceHeaderFooter,
+ LovelaceLayoutOptions,
+} from "../types";
import { HuiErrorCard } from "./hui-error-card";
import { EntityCardConfig } from "./types";
@@ -241,6 +245,15 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ return {
+ grid_columns: 2,
+ grid_rows: 2,
+ grid_min_columns: 2,
+ grid_min_rows: 2,
+ };
+ }
+
static get styles(): CSSResultGroup {
return [
iconColorCSS,
diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts
index f964148c0a64..405158847a09 100644
--- a/src/panels/lovelace/cards/hui-humidifier-card.ts
+++ b/src/panels/lovelace/cards/hui-humidifier-card.ts
@@ -22,7 +22,11 @@ import { HomeAssistant } from "../../../types";
import "../card-features/hui-card-features";
import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning";
-import { LovelaceCard, LovelaceCardEditor } from "../types";
+import {
+ LovelaceCard,
+ LovelaceCardEditor,
+ LovelaceLayoutOptions,
+} from "../types";
import { HumidifierCardConfig } from "./types";
@customElement("hui-humidifier-card")
@@ -173,6 +177,24 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
`;
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ const grid_columns = 4;
+ let grid_rows = 5;
+ let grid_min_rows = 2;
+ const grid_min_columns = 2;
+ if (this._config?.features?.length) {
+ const featureHeight = Math.ceil((this._config.features.length * 2) / 3);
+ grid_rows += featureHeight;
+ grid_min_rows += featureHeight;
+ }
+ return {
+ grid_columns,
+ grid_rows,
+ grid_min_rows,
+ grid_min_columns,
+ };
+ }
+
static get styles(): CSSResultGroup {
return css`
:host {
diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts
index c87766cbae64..36cdea605ec3 100644
--- a/src/panels/lovelace/cards/hui-iframe-card.ts
+++ b/src/panels/lovelace/cards/hui-iframe-card.ts
@@ -119,6 +119,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard {
return {
grid_columns: 4,
grid_rows: 4,
+ grid_min_rows: 2,
};
}
diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts
index ef0d25be529e..0c810280c29c 100644
--- a/src/panels/lovelace/cards/hui-map-card.ts
+++ b/src/panels/lovelace/cards/hui-map-card.ts
@@ -432,6 +432,8 @@ class HuiMapCard extends LitElement implements LovelaceCard {
return {
grid_columns: 4,
grid_rows: 4,
+ grid_min_columns: 2,
+ grid_min_rows: 2,
};
}
diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts
index ffdfe2c34a2f..4cd3e0f1058d 100644
--- a/src/panels/lovelace/cards/hui-media-control-card.ts
+++ b/src/panels/lovelace/cards/hui-media-control-card.ts
@@ -40,7 +40,11 @@ import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-marquee";
import { createEntityNotFoundWarning } from "../components/hui-warning";
-import type { LovelaceCard, LovelaceCardEditor } from "../types";
+import type {
+ LovelaceCard,
+ LovelaceCardEditor,
+ LovelaceLayoutOptions,
+} from "../types";
import { MediaControlCardConfig } from "./types";
@customElement("hui-media-control-card")
@@ -582,6 +586,15 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
}
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ return {
+ grid_columns: 4,
+ grid_min_columns: 2,
+ grid_rows: 3,
+ grid_min_rows: 3,
+ };
+ }
+
static get styles(): CSSResultGroup {
return css`
ha-card {
diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts
index 76ba0fa63626..c456d427b6ab 100644
--- a/src/panels/lovelace/cards/hui-sensor-card.ts
+++ b/src/panels/lovelace/cards/hui-sensor-card.ts
@@ -76,6 +76,8 @@ class HuiSensorCard extends HuiEntityCard {
return {
grid_columns: 2,
grid_rows: 2,
+ grid_min_columns: 2,
+ grid_min_rows: 2,
};
}
diff --git a/src/panels/lovelace/cards/hui-statistic-card.ts b/src/panels/lovelace/cards/hui-statistic-card.ts
index e5beb4a209cc..62338e7a65f3 100644
--- a/src/panels/lovelace/cards/hui-statistic-card.ts
+++ b/src/panels/lovelace/cards/hui-statistic-card.ts
@@ -1,10 +1,10 @@
import { HassEntity } from "home-assistant-js-websocket";
import {
- css,
CSSResultGroup,
- html,
LitElement,
PropertyValues,
+ css,
+ html,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
@@ -16,12 +16,12 @@ import "../../../components/ha-alert";
import "../../../components/ha-card";
import "../../../components/ha-state-icon";
import {
+ StatisticsMetaData,
fetchStatistic,
getDisplayUnit,
getStatisticLabel,
getStatisticMetadata,
isExternalStatistic,
- StatisticsMetaData,
} from "../../../data/recorder";
import { HomeAssistant } from "../../../types";
import { computeCardSize } from "../common/compute-card-size";
@@ -32,6 +32,7 @@ import {
LovelaceCard,
LovelaceCardEditor,
LovelaceHeaderFooter,
+ LovelaceLayoutOptions,
} from "../types";
import { HuiErrorCard } from "./hui-error-card";
import { EntityCardConfig, StatisticCardConfig } from "./types";
@@ -254,6 +255,15 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard {
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ return {
+ grid_columns: 2,
+ grid_rows: 2,
+ grid_min_columns: 2,
+ grid_min_rows: 2,
+ };
+ }
+
static get styles(): CSSResultGroup {
return [
css`
diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts
index d8d58ee9c776..620f17a20562 100644
--- a/src/panels/lovelace/cards/hui-thermostat-card.ts
+++ b/src/panels/lovelace/cards/hui-thermostat-card.ts
@@ -22,7 +22,11 @@ import { HomeAssistant } from "../../../types";
import "../card-features/hui-card-features";
import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning";
-import { LovelaceCard, LovelaceCardEditor } from "../types";
+import {
+ LovelaceCard,
+ LovelaceCardEditor,
+ LovelaceLayoutOptions,
+} from "../types";
import { ThermostatCardConfig } from "./types";
@customElement("hui-thermostat-card")
@@ -165,6 +169,24 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
`;
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ const grid_columns = 4;
+ let grid_rows = 5;
+ let grid_min_rows = 2;
+ const grid_min_columns = 2;
+ if (this._config?.features?.length) {
+ const featureHeight = Math.ceil((this._config.features.length * 2) / 3);
+ grid_rows += featureHeight;
+ grid_min_rows += featureHeight;
+ }
+ return {
+ grid_columns,
+ grid_rows,
+ grid_min_rows,
+ grid_min_columns,
+ };
+ }
+
static get styles(): CSSResultGroup {
return css`
:host {
diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts
index fa8c2cc6d067..19a05e3bb4ab 100644
--- a/src/panels/lovelace/cards/hui-tile-card.ts
+++ b/src/panels/lovelace/cards/hui-tile-card.ts
@@ -122,17 +122,21 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
}
public getLayoutOptions(): LovelaceLayoutOptions {
- const options = {
- grid_columns: 2,
- grid_rows: 1,
- };
+ const grid_columns = 2;
+ let grid_rows = 1;
if (this._config?.features?.length) {
- options.grid_rows += Math.ceil((this._config.features.length * 2) / 3);
+ const featureHeight = Math.ceil((this._config.features.length * 2) / 3);
+ grid_rows += featureHeight;
}
if (this._config?.vertical) {
- options.grid_rows++;
+ grid_rows!++;
}
- return options;
+ return {
+ grid_columns,
+ grid_rows,
+ grid_min_rows: grid_rows,
+ grid_min_columns: grid_columns,
+ };
}
private _handleAction(ev: ActionHandlerEvent) {
diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts
index 40649ed32ae2..fadca1dd5aef 100644
--- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts
+++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts
@@ -38,7 +38,11 @@ import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning";
-import type { LovelaceCard, LovelaceCardEditor } from "../types";
+import type {
+ LovelaceCard,
+ LovelaceCardEditor,
+ LovelaceLayoutOptions,
+} from "../types";
import type { WeatherForecastCardConfig } from "./types";
@customElement("hui-weather-forecast-card")
@@ -421,6 +425,26 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
return typeof item !== "undefined" && item !== null;
}
+ public getLayoutOptions(): LovelaceLayoutOptions {
+ if (
+ this._config?.show_current !== false &&
+ this._config?.show_forecast !== false
+ ) {
+ return {
+ grid_columns: 4,
+ grid_min_columns: 2,
+ grid_rows: 3,
+ grid_min_rows: 3,
+ };
+ }
+ return {
+ grid_columns: 4,
+ grid_min_columns: 2,
+ grid_rows: 2,
+ grid_min_rows: 1,
+ };
+ }
+
static get styles(): CSSResultGroup {
return [
weatherSVGStyles,
diff --git a/src/panels/lovelace/editor/card-editor/ha-grid-layout-slider.ts b/src/panels/lovelace/editor/card-editor/ha-grid-layout-slider.ts
index 2ef6a61f0c40..ab1bebf00fcc 100644
--- a/src/panels/lovelace/editor/card-editor/ha-grid-layout-slider.ts
+++ b/src/panels/lovelace/editor/card-editor/ha-grid-layout-slider.ts
@@ -255,15 +255,14 @@ export class HaGridLayoutSlider extends LitElement {
>
${this.value !== undefined
? html`
`
@@ -323,11 +322,12 @@ export class HaGridLayoutSlider extends LitElement {
position: absolute;
inset: 0;
background: var(--disabled-color);
- opacity: 0.5;
+ opacity: 0.2;
}
.active {
position: absolute;
background: grey;
+ opacity: 0.7;
top: 0;
right: calc(var(--max) * 100%);
bottom: 0;
@@ -375,6 +375,9 @@ export class HaGridLayoutSlider extends LitElement {
:host(:disabled) .slider {
cursor: not-allowed;
}
+ :host(:disabled) .handle:after {
+ background: var(--disabled-color);
+ }
.pressed .handle {
transition: none;
}
diff --git a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts
index 68c0487beee4..5a5690727ce6 100644
--- a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts
+++ b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts
@@ -19,7 +19,7 @@ import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { HuiCard } from "../../cards/hui-card";
-import { DEFAULT_GRID_OPTIONS } from "../../sections/hui-grid-section";
+import { computeSizeOnGrid } from "../../sections/hui-grid-section";
import { LovelaceLayoutOptions } from "../../types";
@customElement("hui-card-layout-editor")
@@ -38,28 +38,29 @@ export class HuiCardLayoutEditor extends LitElement {
private _cardElement?: HuiCard;
- private _gridSizeValue = memoizeOne(
+ private _mergedOptions = memoizeOne(
(
options?: LovelaceLayoutOptions,
defaultOptions?: LovelaceLayoutOptions
) => ({
- rows:
- options?.grid_rows ??
- defaultOptions?.grid_rows ??
- DEFAULT_GRID_OPTIONS.grid_rows,
- columns:
- options?.grid_columns ??
- defaultOptions?.grid_columns ??
- DEFAULT_GRID_OPTIONS.grid_columns,
+ ...defaultOptions,
+ ...options,
})
);
+ private _gridSizeValue = memoizeOne(computeSizeOnGrid);
+
private _isDefault = memoizeOne(
(options?: LovelaceLayoutOptions) =>
options?.grid_columns === undefined && options?.grid_rows === undefined
);
render() {
+ const options = this._mergedOptions(
+ this.config.layout_options,
+ this._defaultLayoutOptions
+ );
+
return html`