diff --git a/src/components/ha-grid-size-picker.ts b/src/components/ha-grid-size-picker.ts
index f62fc5c01d..30fdf1a9f9 100644
--- a/src/components/ha-grid-size-picker.ts
+++ b/src/components/ha-grid-size-picker.ts
@@ -1,24 +1,24 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
-import "./ha-icon-button";
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
+import "./ha-icon-button";
import { mdiRestore } from "@mdi/js";
+import { classMap } from "lit/directives/class-map";
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 | "auto";
- columns?: number;
-};
+import {
+ CardGridSize,
+ DEFAULT_GRID_SIZE,
+} from "../panels/lovelace/common/compute-card-grid-size";
+import { HomeAssistant } from "../types";
@customElement("ha-grid-size-picker")
export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
- @property({ attribute: false }) public value?: GridSizeValue;
+ @property({ attribute: false }) public value?: CardGridSize;
@property({ attribute: false }) public rows = 8;
@@ -34,7 +34,7 @@ export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public isDefault?: boolean;
- @state() public _localValue?: GridSizeValue = undefined;
+ @state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
protected willUpdate(changedProperties) {
if (changedProperties.has("value")) {
@@ -49,6 +49,7 @@ export class HaGridSizeEditor extends LitElement {
this.rowMin !== undefined && this.rowMin === this.rowMax;
const autoHeight = this._localValue?.rows === "auto";
+ const fullWidth = this._localValue?.columns === "full";
const rowMin = this.rowMin ?? 1;
const rowMax = this.rowMax ?? this.rows;
@@ -67,7 +68,7 @@ export class HaGridSizeEditor extends LitElement {
.min=${columnMin}
.max=${columnMax}
.range=${this.columns}
- .value=${columnValue}
+ .value=${fullWidth ? this.columns : columnValue}
@value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved}
.disabled=${disabledColumns}
@@ -104,12 +105,12 @@ export class HaGridSizeEditor extends LitElement {
`
: nothing}
@@ -140,12 +141,21 @@ export class HaGridSizeEditor extends LitElement {
const cell = ev.currentTarget as HTMLElement;
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(
+ const clampedRow: CardGridSize["rows"] = conditionalClamp(
+ rows,
+ this.rowMin,
+ this.rowMax
+ );
+ let clampedColumn: CardGridSize["columns"] = conditionalClamp(
columns,
this.columnMin,
this.columnMax
);
+
+ const currentSize = this.value ?? DEFAULT_GRID_SIZE;
+ if (currentSize.columns === "full" && clampedColumn === this.columns) {
+ clampedColumn = "full";
+ }
fireEvent(this, "value-changed", {
value: { rows: clampedRow, columns: clampedColumn },
});
@@ -153,12 +163,23 @@ export class HaGridSizeEditor extends LitElement {
private _valueChanged(ev) {
ev.stopPropagation();
- const key = ev.currentTarget.id;
- const newValue = {
- ...this.value,
- [key]: ev.detail.value,
+ const key = ev.currentTarget.id as "rows" | "columns";
+ const currentSize = this.value ?? DEFAULT_GRID_SIZE;
+ let value = ev.detail.value as CardGridSize[typeof key];
+
+ if (
+ key === "columns" &&
+ currentSize.columns === "full" &&
+ value === this.columns
+ ) {
+ value = "full";
+ }
+
+ const newSize = {
+ ...currentSize,
+ [key]: value,
};
- fireEvent(this, "value-changed", { value: newValue });
+ fireEvent(this, "value-changed", { value: newSize });
}
private _reset(ev) {
@@ -173,11 +194,14 @@ export class HaGridSizeEditor extends LitElement {
private _sliderMoved(ev) {
ev.stopPropagation();
- const key = ev.currentTarget.id;
- const value = ev.detail.value;
+ const key = ev.currentTarget.id as "rows" | "columns";
+ const currentSize = this.value ?? DEFAULT_GRID_SIZE;
+ const value = ev.detail.value as CardGridSize[typeof key] | undefined;
+
if (value === undefined) return;
+
this._localValue = {
- ...this.value,
+ ...currentSize,
[key]: ev.detail.value,
};
}
@@ -231,10 +255,13 @@ export class HaGridSizeEditor extends LitElement {
}
.selected .cell {
background-color: var(--primary-color);
- grid-column: 1 / span var(--columns, 0);
- grid-row: 1 / span var(--rows, 0);
+ grid-column: 1 / span min(var(--columns, 0), var(--total-columns));
+ grid-row: 1 / span min(var(--rows, 0), var(--total-rows));
opacity: 0.5;
}
+ .preview.full-width .selected .cell {
+ grid-column: 1 / -1;
+ }
`,
];
}
diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts
index cc2cdeade5..b70af9a7ab 100644
--- a/src/panels/lovelace/cards/hui-iframe-card.ts
+++ b/src/panels/lovelace/cards/hui-iframe-card.ts
@@ -114,7 +114,7 @@ export class HuiIframeCard extends LitElement implements LovelaceCard {
public getLayoutOptions(): LovelaceLayoutOptions {
return {
- grid_columns: 4,
+ grid_columns: "full",
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 17e6df279f..ae3bd52798 100644
--- a/src/panels/lovelace/cards/hui-map-card.ts
+++ b/src/panels/lovelace/cards/hui-map-card.ts
@@ -426,7 +426,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
public getLayoutOptions(): LovelaceLayoutOptions {
return {
- grid_columns: 4,
+ grid_columns: "full",
grid_rows: 4,
grid_min_columns: 2,
grid_min_rows: 2,
diff --git a/src/panels/lovelace/common/compute-card-grid-size.ts b/src/panels/lovelace/common/compute-card-grid-size.ts
new file mode 100644
index 0000000000..c27b5a55ba
--- /dev/null
+++ b/src/panels/lovelace/common/compute-card-grid-size.ts
@@ -0,0 +1,36 @@
+import { conditionalClamp } from "../../../common/number/clamp";
+import { LovelaceLayoutOptions } from "../types";
+
+export const DEFAULT_GRID_SIZE = {
+ columns: 4,
+ rows: "auto",
+} as CardGridSize;
+
+export type CardGridSize = {
+ rows: number | "auto";
+ columns: number | "full";
+};
+
+export const computeCardGridSize = (
+ options: LovelaceLayoutOptions
+): CardGridSize => {
+ const rows = options.grid_rows ?? DEFAULT_GRID_SIZE.rows;
+ const columns = options.grid_columns ?? DEFAULT_GRID_SIZE.columns;
+ const minRows = options.grid_min_rows;
+ const maxRows = options.grid_max_rows;
+ const minColumns = options.grid_min_columns;
+ const maxColumns = options.grid_max_columns;
+
+ const clampedRows =
+ typeof rows === "string" ? rows : conditionalClamp(rows, minRows, maxRows);
+
+ const clampedColumns =
+ typeof columns === "string"
+ ? columns
+ : conditionalClamp(columns, minColumns, maxColumns);
+
+ return {
+ rows: clampedRows,
+ columns: clampedColumns,
+ };
+};
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 ac32343f75..d944d94609 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
@@ -1,6 +1,6 @@
import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiDotsVertical } from "@mdi/js";
-import { LitElement, PropertyValues, css, html, nothing } from "lit";
+import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -8,6 +8,7 @@ import { preventDefault } from "../../../../common/dom/prevent_default";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import "../../../../components/ha-button";
import "../../../../components/ha-button-menu";
+import "../../../../components/ha-formfield";
import "../../../../components/ha-grid-size-picker";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-list-item";
@@ -21,7 +22,10 @@ import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { HuiCard } from "../../cards/hui-card";
-import { computeSizeOnGrid } from "../../sections/hui-grid-section";
+import {
+ CardGridSize,
+ computeCardGridSize,
+} from "../../common/compute-card-grid-size";
import { LovelaceLayoutOptions } from "../../types";
@customElement("hui-card-layout-editor")
@@ -50,7 +54,7 @@ export class HuiCardLayoutEditor extends LitElement {
})
);
- private _gridSizeValue = memoizeOne(computeSizeOnGrid);
+ private _computeCardGridSize = memoizeOne(computeCardGridSize);
private _isDefault = memoizeOne(
(options?: LovelaceLayoutOptions) =>
@@ -63,7 +67,7 @@ export class HuiCardLayoutEditor extends LitElement {
this._defaultLayoutOptions
);
- const sizeValue = this._gridSizeValue(options);
+ const value = this._computeCardGridSize(options);
return html`