From effefdbff1e591b1e44c136d65a2b8400ef3cce8 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 7 Mar 2024 11:22:26 +0100 Subject: [PATCH] Add layout options to cards and improve grid and sections defaults (#20001) * Add grid options to cards * Fix the height of the card it's rows option is provided * Add variable * Adjust grid margin * Use layout options * Fix max width when only one column * Update card API --- src/data/lovelace/config/card.ts | 3 ++ src/panels/lovelace/cards/hui-button-card.ts | 12 ++++-- src/panels/lovelace/cards/hui-sensor-card.ts | 11 +++-- src/panels/lovelace/cards/hui-tile-card.ts | 20 +++++---- .../editor/structs/base-card-struct.ts | 1 + .../lovelace/sections/hui-grid-section.ts | 42 ++++++++++++++----- src/panels/lovelace/types.ts | 7 +++- .../lovelace/views/hui-sections-view.ts | 22 +++++----- 8 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/data/lovelace/config/card.ts b/src/data/lovelace/config/card.ts index f41fc1e084..414e0826ba 100644 --- a/src/data/lovelace/config/card.ts +++ b/src/data/lovelace/config/card.ts @@ -1,7 +1,10 @@ +import { LovelaceLayoutOptions } from "../../../panels/lovelace/types"; + export interface LovelaceCardConfig { index?: number; view_index?: number; view_layout?: any; + layout_options?: LovelaceLayoutOptions; type: string; [key: string]: any; } diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 3a37329b27..ddadb6d86f 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -52,7 +52,11 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entities"; import { hasAction } from "../common/has-action"; import { createEntityNotFoundWarning } from "../components/hui-warning"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; +import { + LovelaceCard, + LovelaceCardEditor, + LovelaceLayoutOptions, +} from "../types"; import { ButtonCardConfig } from "./types"; export const getEntityDefaultButtonAction = (entityId?: string) => @@ -143,14 +147,14 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { ); } - public getGridSize(): [number, number] { + public getLayoutOptions(): LovelaceLayoutOptions { if ( this._config?.show_icon && (this._config?.show_name || this._config?.show_state) ) { - return [2, 2]; + return { grid_rows: 2, grid_columns: 2 }; } - return [1, 1]; + return { grid_rows: 1, grid_columns: 1 }; } public setConfig(config: ButtonCardConfig): void { diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts index 709a6591d9..76ba0fa636 100644 --- a/src/panels/lovelace/cards/hui-sensor-card.ts +++ b/src/panels/lovelace/cards/hui-sensor-card.ts @@ -1,11 +1,11 @@ -import { css, CSSResultGroup } from "lit"; import { HassEntity } from "home-assistant-js-websocket/dist/types"; +import { css, CSSResultGroup } from "lit"; import { customElement } from "lit/decorators"; import { computeDomain } from "../../../common/entity/compute_domain"; import { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; import { GraphHeaderFooterConfig } from "../header-footer/types"; -import { LovelaceCardEditor } from "../types"; +import { LovelaceCardEditor, LovelaceLayoutOptions } from "../types"; import { HuiEntityCard } from "./hui-entity-card"; import { EntityCardConfig, SensorCardConfig } from "./types"; @@ -72,8 +72,11 @@ class HuiSensorCard extends HuiEntityCard { super.setConfig(entityCardConfig); } - public getGridSize(): [number, number] { - return [2, 2]; + public getLayoutOptions(): LovelaceLayoutOptions { + return { + grid_columns: 2, + grid_rows: 2, + }; } static get styles(): CSSResultGroup { diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 1d56cc66bd..abb180dd35 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -48,7 +48,11 @@ import { findEntities } from "../common/find-entities"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import "../components/hui-timestamp-display"; -import type { LovelaceCard, LovelaceCardEditor } from "../types"; +import type { + LovelaceCard, + LovelaceCardEditor, + LovelaceLayoutOptions, +} from "../types"; import { renderTileBadge } from "./tile/badges/tile-badge"; import type { ThermostatCardConfig, TileCardConfig } from "./types"; @@ -124,16 +128,18 @@ export class HuiTileCard extends LitElement implements LovelaceCard { ); } - public getGridSize(): [number, number] { - const width = 2; - let height = 1; + public getLayoutOptions(): LovelaceLayoutOptions { + const options = { + grid_columns: 2, + grid_rows: 1, + }; if (this._config?.features?.length) { - height += Math.ceil((this._config.features.length * 2) / 3); + options.grid_rows += Math.ceil((this._config.features.length * 2) / 3); } if (this._config?.vertical) { - height++; + options.grid_rows++; } - return [width, height]; + return options; } private _handleAction(ev: ActionHandlerEvent) { diff --git a/src/panels/lovelace/editor/structs/base-card-struct.ts b/src/panels/lovelace/editor/structs/base-card-struct.ts index 49e18c3b05..85d7685e10 100644 --- a/src/panels/lovelace/editor/structs/base-card-struct.ts +++ b/src/panels/lovelace/editor/structs/base-card-struct.ts @@ -3,4 +3,5 @@ import { object, string, any } from "superstruct"; export const baseLovelaceCardConfig = object({ type: string(), view_layout: any(), + layout_options: any(), }); diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index 83467239ac..d929eabcf1 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -14,7 +14,7 @@ import type { HomeAssistant } from "../../../types"; import { HuiErrorCard } from "../cards/hui-error-card"; import "../components/hui-card-edit-mode"; import { moveCard } from "../editor/config-util"; -import type { Lovelace, LovelaceCard } from "../types"; +import type { Lovelace, LovelaceCard, LovelaceLayoutOptions } from "../types"; const CARD_SORTABLE_OPTIONS: HaSortableOptions = { delay: 100, @@ -97,14 +97,26 @@ export class GridSection extends LitElement implements LovelaceSectionElement { const card = this.cards![idx]; (card as any).editMode = editMode; (card as any).lovelace = this.lovelace; - const size = card && (card as any).getGridSize?.(); + + const configOptions = _cardConfig.layout_options; + const cardOptions = (card as any)?.getLayoutOptions?.() as + | LovelaceLayoutOptions + | undefined; + + const options = { + ...cardOptions, + ...configOptions, + } as LovelaceLayoutOptions; + return html`
${editMode ? html` @@ -171,16 +183,18 @@ export class GridSection extends LitElement implements LovelaceSectionElement { haStyle, css` :host { + --grid-gap: 8px; + --grid-row-height: 66px; display: flex; flex-direction: column; - gap: 8px; + gap: var(--grid-gap); } .container { --column-count: 4; display: grid; grid-template-columns: repeat(var(--column-count), minmax(0, 1fr)); - grid-auto-rows: minmax(66px, auto); - gap: 8px; + grid-auto-rows: minmax(var(--grid-row-height), auto); + gap: var(--grid-gap); padding: 0; margin: 0 auto; } @@ -189,7 +203,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement { padding: 8px; border-radius: var(--ha-card-border-radius, 12px); border: 2px dashed var(--divider-color); - min-height: 66px; + min-height: var(var(--grid-row-height)); } .title { @@ -216,6 +230,14 @@ export class GridSection extends LitElement implements LovelaceSectionElement { grid-column: span var(--column-size, 4); } + .card.fit-rows { + height: calc( + (var(--row-size, 1) * (var(--grid-row-height) + var(--grid-gap))) - var( + --grid-gap + ) + ); + } + .card:has(> *) { display: block; } @@ -232,7 +254,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement { cursor: pointer; border-radius: var(--ha-card-border-radius, 12px); border: 2px dashed var(--primary-color); - height: 66px; + height: var(--grid-row-height); order: 1; } .add:focus { diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index 31a87961f2..bf7709f612 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -39,12 +39,17 @@ export interface LovelaceBadge extends HTMLElement { setConfig(config: LovelaceBadgeConfig): void; } +export type LovelaceLayoutOptions = { + grid_columns?: number; + grid_rows?: number; +}; + export interface LovelaceCard extends HTMLElement { hass?: HomeAssistant; isPanel?: boolean; editMode?: boolean; getCardSize(): number | Promise; - getGridSize?(): [number, number]; + getLayoutOptions?(): LovelaceLayoutOptions; setConfig(config: LovelaceCardConfig): void; } diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index aeea8ae5b3..79c3325109 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -234,11 +234,15 @@ export class SectionsView extends LitElement implements LovelaceViewElement { static get styles(): CSSResultGroup { return css` :host { + --grid-gap: 32px; + --grid-max-section-count: 4; + --grid-section-min-width: 320px; + --grid-section-max-width: 500px; display: block; } .badges { - margin: 12px 8px 16px 8px; + margin: 12px 8px 4px 8px; font-size: 85%; text-align: center; } @@ -246,18 +250,13 @@ export class SectionsView extends LitElement implements LovelaceViewElement { .section { position: relative; border-radius: var(--ha-card-border-radius, 12px); + max-width: var(--grid-section-max-width); + width: 100%; } .container { - /* Inputs */ - --grid-gap: 32px; - --grid-max-section-count: 4; - --grid-section-min-width: 320px; - --grid-section-max-width: 500px; - - /* Calculated */ --max-count: min(var(--section-count), var(--grid-max-section-count)); - --grid-max-width: min( + --max-width: min( calc( (var(--max-count) + 1) * var(--grid-section-min-width) + (var(--max-count) + 2) * var(--grid-gap) - 1px @@ -267,8 +266,9 @@ export class SectionsView extends LitElement implements LovelaceViewElement { (var(--max-count) + 1) * var(--grid-gap) ) ); - display: grid; + align-items: start; + justify-items: center; grid-template-columns: repeat( auto-fit, minmax(min(var(--grid-section-min-width), 100%), 1fr) @@ -276,7 +276,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement { grid-gap: 8px var(--grid-gap); padding: 8px var(--grid-gap); box-sizing: border-box; - max-width: var(--grid-max-width); + max-width: var(--max-width); margin: 0 auto; }