Add precise resizing mode for card inside section (#22366)

* Allow to resize card in the grid with more precision

* Use 12 columns grid

* Fix cursor jump when dragging slider

* Remove precision mode

* Add precise mode switch

* Add switch between regular and precise mode

* Fix prettier

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
This commit is contained in:
Paul Bottein 2024-10-30 14:35:28 +01:00 committed by GitHub
parent 29d9b61319
commit c0ca7e425e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 270 additions and 104 deletions

View File

@ -66,7 +66,7 @@ export class HaGridSizeEditor extends LitElement {
.min=${columnMin} .min=${columnMin}
.max=${columnMax} .max=${columnMax}
.range=${this.columns} .range=${this.columns}
.value=${fullWidth ? this.columns : columnValue} .value=${fullWidth ? this.columns : this.value?.columns}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved} @slider-moved=${this._sliderMoved}
.disabled=${disabledColumns} .disabled=${disabledColumns}
@ -81,7 +81,7 @@ export class HaGridSizeEditor extends LitElement {
.max=${rowMax} .max=${rowMax}
.range=${this.rows} .range=${this.rows}
vertical vertical
.value=${rowValue} .value=${autoHeight ? rowMin : this.value?.rows}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@slider-moved=${this._sliderMoved} @slider-moved=${this._sliderMoved}
.disabled=${disabledRows} .disabled=${disabledRows}

View File

@ -1,11 +1,16 @@
import type { Condition } from "../../../panels/lovelace/common/validate-condition"; import type { Condition } from "../../../panels/lovelace/common/validate-condition";
import type { LovelaceLayoutOptions } from "../../../panels/lovelace/types"; import type {
LovelaceGridOptions,
LovelaceLayoutOptions,
} from "../../../panels/lovelace/types";
export interface LovelaceCardConfig { export interface LovelaceCardConfig {
index?: number; index?: number;
view_index?: number; view_index?: number;
view_layout?: any; view_layout?: any;
/** @deprecated Use `grid_options` instead */
layout_options?: LovelaceLayoutOptions; layout_options?: LovelaceLayoutOptions;
grid_options?: LovelaceGridOptions;
type: string; type: string;
[key: string]: any; [key: string]: any;
visibility?: Condition[]; visibility?: Condition[];

View File

@ -6,6 +6,7 @@ import type { MediaQueriesListener } from "../../../common/dom/media_query";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { migrateLayoutToGridOptions } from "../common/compute-card-grid-size";
import { computeCardSize } from "../common/compute-card-size"; import { computeCardSize } from "../common/compute-card-size";
import { import {
attachConditionMediaQueriesListeners, attachConditionMediaQueriesListeners,
@ -13,7 +14,7 @@ import {
} from "../common/validate-condition"; } from "../common/validate-condition";
import { createCardElement } from "../create-element/create-card-element"; import { createCardElement } from "../create-element/create-card-element";
import { createErrorCardConfig } from "../create-element/create-element-base"; import { createErrorCardConfig } from "../create-element/create-element-base";
import type { LovelaceCard, LovelaceLayoutOptions } from "../types"; import type { LovelaceCard, LovelaceGridOptions } from "../types";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -68,20 +69,44 @@ export class HuiCard extends ReactiveElement {
return 1; return 1;
} }
public getLayoutOptions(): LovelaceLayoutOptions { public getGridOptions(): LovelaceGridOptions {
const configOptions = this.config?.layout_options ?? {}; const elementOptions = this.getElementGridOptions();
if (this._element) { const configOptions = this.getConfigGridOptions();
const cardOptions = this._element.getLayoutOptions?.() ?? {}; return {
return { ...elementOptions,
...cardOptions, ...configOptions,
...configOptions, };
};
}
return configOptions;
} }
public getElementLayoutOptions(): LovelaceLayoutOptions { // options provided by the element
return this._element?.getLayoutOptions?.() ?? {}; public getElementGridOptions(): LovelaceGridOptions {
if (!this._element) return {};
if (this._element.getGridOptions) {
return this._element.getGridOptions();
}
if (this._element.getLayoutOptions) {
// eslint-disable-next-line no-console
console.warn(
`This card (${this.config?.type}) is using "getLayoutOptions" and it is deprecated, contact the developer to suggest to use "getGridOptions" instead`
);
const options = migrateLayoutToGridOptions(
this._element.getLayoutOptions()
);
return options;
}
return {};
}
// options provided by the config
public getConfigGridOptions(): LovelaceGridOptions {
if (this.config?.grid_options) {
return this.config.grid_options;
}
if (this.config?.layout_options) {
return migrateLayoutToGridOptions(this.config.layout_options);
}
return {};
} }
private _updateElement(config: LovelaceCardConfig) { private _updateElement(config: LovelaceCardConfig) {

View File

@ -1,8 +1,39 @@
import { conditionalClamp } from "../../../common/number/clamp"; import { conditionalClamp } from "../../../common/number/clamp";
import type { LovelaceLayoutOptions } from "../types"; import type { LovelaceGridOptions, LovelaceLayoutOptions } from "../types";
export const GRID_COLUMN_MULTIPLIER = 3;
export const multiplyBy = <T extends number | string | undefined>(
value: T,
multiplier: number
): T => (typeof value === "number" ? ((value * multiplier) as T) : value);
export const divideBy = <T extends number | string | undefined>(
value: T,
divider: number
): T => (typeof value === "number" ? (Math.ceil(value / divider) as T) : value);
export const migrateLayoutToGridOptions = (
options: LovelaceLayoutOptions
): LovelaceGridOptions => {
const gridOptions: LovelaceGridOptions = {
columns: multiplyBy(options.grid_columns, GRID_COLUMN_MULTIPLIER),
max_columns: multiplyBy(options.grid_max_columns, GRID_COLUMN_MULTIPLIER),
min_columns: multiplyBy(options.grid_min_columns, GRID_COLUMN_MULTIPLIER),
rows: options.grid_rows,
max_rows: options.grid_max_rows,
min_rows: options.grid_min_rows,
};
for (const [key, value] of Object.entries(gridOptions)) {
if (value === undefined) {
delete gridOptions[key];
}
}
return gridOptions;
};
export const DEFAULT_GRID_SIZE = { export const DEFAULT_GRID_SIZE = {
columns: 4, columns: 12,
rows: "auto", rows: "auto",
} as CardGridSize; } as CardGridSize;
@ -12,14 +43,14 @@ export type CardGridSize = {
}; };
export const computeCardGridSize = ( export const computeCardGridSize = (
options: LovelaceLayoutOptions options: LovelaceGridOptions
): CardGridSize => { ): CardGridSize => {
const rows = options.grid_rows ?? DEFAULT_GRID_SIZE.rows; const rows = options.rows ?? DEFAULT_GRID_SIZE.rows;
const columns = options.grid_columns ?? DEFAULT_GRID_SIZE.columns; const columns = options.columns ?? DEFAULT_GRID_SIZE.columns;
const minRows = options.grid_min_rows; const minRows = options.min_rows;
const maxRows = options.grid_max_rows; const maxRows = options.max_rows;
const minColumns = options.grid_min_columns; const minColumns = options.min_columns;
const maxColumns = options.grid_max_columns; const maxColumns = options.max_columns;
const clampedRows = const clampedRows =
typeof rows === "string" ? rows : conditionalClamp(rows, minRows, maxRows); typeof rows === "string" ? rows : conditionalClamp(rows, minRows, maxRows);

View File

@ -2,7 +2,7 @@ import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiDotsVertical } from "@mdi/js"; import { mdiCheck, mdiDotsVertical } from "@mdi/js";
import type { PropertyValues } from "lit"; import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
@ -18,15 +18,23 @@ import "../../../../components/ha-slider";
import "../../../../components/ha-svg-icon"; import "../../../../components/ha-svg-icon";
import "../../../../components/ha-switch"; import "../../../../components/ha-switch";
import "../../../../components/ha-yaml-editor"; import "../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { haStyle } from "../../../../resources/styles"; import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { HuiCard } from "../../cards/hui-card"; import type { HuiCard } from "../../cards/hui-card";
import type { CardGridSize } from "../../common/compute-card-grid-size"; import type { CardGridSize } from "../../common/compute-card-grid-size";
import { computeCardGridSize } from "../../common/compute-card-grid-size"; import {
import type { LovelaceLayoutOptions } from "../../types"; computeCardGridSize,
divideBy,
GRID_COLUMN_MULTIPLIER,
migrateLayoutToGridOptions,
multiplyBy,
} from "../../common/compute-card-grid-size";
import type { LovelaceGridOptions } from "../../types";
const computePreciseMode = (columns?: number | string) =>
typeof columns === "number" && columns % 3 !== 0;
@customElement("hui-card-layout-editor") @customElement("hui-card-layout-editor")
export class HuiCardLayoutEditor extends LitElement { export class HuiCardLayoutEditor extends LitElement {
@ -36,21 +44,18 @@ export class HuiCardLayoutEditor extends LitElement {
@property({ attribute: false }) public sectionConfig!: LovelaceSectionConfig; @property({ attribute: false }) public sectionConfig!: LovelaceSectionConfig;
@state() _defaultLayoutOptions?: LovelaceLayoutOptions; @state() private _defaultGridOptions?: LovelaceGridOptions;
@state() public _yamlMode = false; @state() private _yamlMode = false;
@state() public _uiAvailable = true; @state() private _uiAvailable = true;
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; @state() private _preciseMode = false;
private _cardElement?: HuiCard; private _cardElement?: HuiCard;
private _mergedOptions = memoizeOne( private _mergedOptions = memoizeOne(
( (options?: LovelaceGridOptions, defaultOptions?: LovelaceGridOptions) => ({
options?: LovelaceLayoutOptions,
defaultOptions?: LovelaceLayoutOptions
) => ({
...defaultOptions, ...defaultOptions,
...options, ...options,
}) })
@ -58,20 +63,52 @@ export class HuiCardLayoutEditor extends LitElement {
private _computeCardGridSize = memoizeOne(computeCardGridSize); private _computeCardGridSize = memoizeOne(computeCardGridSize);
private _simplifyOptions = (
options: LovelaceGridOptions
): LovelaceGridOptions => ({
...options,
columns: divideBy(options.columns, GRID_COLUMN_MULTIPLIER),
max_columns: divideBy(options.max_columns, GRID_COLUMN_MULTIPLIER),
min_columns: divideBy(options.min_columns, GRID_COLUMN_MULTIPLIER),
});
private _standardizeOptions = (options: LovelaceGridOptions) => ({
...options,
columns: multiplyBy(options.columns, GRID_COLUMN_MULTIPLIER),
max_columns: multiplyBy(options.max_columns, GRID_COLUMN_MULTIPLIER),
min_columns: multiplyBy(options.min_columns, GRID_COLUMN_MULTIPLIER),
});
private _isDefault = memoizeOne( private _isDefault = memoizeOne(
(options?: LovelaceLayoutOptions) => (options?: LovelaceGridOptions) =>
options?.grid_columns === undefined && options?.grid_rows === undefined options?.columns === undefined && options?.rows === undefined
); );
private _configGridOptions = (config: LovelaceCardConfig) => {
if (config.grid_options) {
return config.grid_options;
}
if (config.layout_options) {
return migrateLayoutToGridOptions(config.layout_options);
}
return {};
};
render() { render() {
const configOptions = this._configGridOptions(this.config);
const options = this._mergedOptions( const options = this._mergedOptions(
this.config.layout_options, configOptions,
this._defaultLayoutOptions this._defaultGridOptions
); );
const value = this._computeCardGridSize(options); const gridOptions = this._preciseMode
? options
: this._simplifyOptions(options);
const gridValue = this._computeCardGridSize(gridOptions);
const totalColumns = (this.sectionConfig.column_span ?? 1) * 4; const columnSpan = this.sectionConfig.column_span ?? 1;
const gridTotalColumns =
(12 * columnSpan) / (this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER);
return html` return html`
<div class="header"> <div class="header">
@ -129,24 +166,24 @@ export class HuiCardLayoutEditor extends LitElement {
? html` ? html`
<ha-yaml-editor <ha-yaml-editor
.hass=${this.hass} .hass=${this.hass}
.defaultValue=${this.config.layout_options} .defaultValue=${configOptions}
@value-changed=${this._valueChanged} @value-changed=${this._yamlChanged}
></ha-yaml-editor> ></ha-yaml-editor>
` `
: html` : html`
<ha-grid-size-picker <ha-grid-size-picker
style=${styleMap({ style=${styleMap({
"max-width": `${totalColumns * 45 + 50}px`, "max-width": `${(this.sectionConfig.column_span ?? 1) * 200 + 50}px`,
})} })}
.columns=${totalColumns} .columns=${gridTotalColumns}
.hass=${this.hass} .hass=${this.hass}
.value=${value} .value=${gridValue}
.isDefault=${this._isDefault(this.config.layout_options)} .isDefault=${this._isDefault(configOptions)}
@value-changed=${this._gridSizeChanged} @value-changed=${this._gridSizeChanged}
.rowMin=${options.grid_min_rows} .rowMin=${gridOptions.min_rows}
.rowMax=${options.grid_max_rows} .rowMax=${gridOptions.max_rows}
.columnMin=${options.grid_min_columns} .columnMin=${gridOptions.min_columns}
.columnMax=${options.grid_max_columns} .columnMax=${gridOptions.max_columns}
></ha-grid-size-picker> ></ha-grid-size-picker>
<ha-settings-row> <ha-settings-row>
<span slot="heading" data-for="full-width"> <span slot="heading" data-for="full-width">
@ -161,11 +198,29 @@ export class HuiCardLayoutEditor extends LitElement {
</span> </span>
<ha-switch <ha-switch
@change=${this._fullWidthChanged} @change=${this._fullWidthChanged}
.checked=${value.columns === "full"} .checked=${options.columns === "full"}
name="full-width" name="full-width"
> >
</ha-switch> </ha-switch>
</ha-settings-row> </ha-settings-row>
<ha-settings-row>
<span slot="heading" data-for="precise-mode">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.precise_mode"
)}
</span>
<span slot="description" data-for="precise-mode">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.precise_mode_helper"
)}
</span>
<ha-switch
@change=${this._preciseModeChanged}
.checked=${this._preciseMode}
name="precise-mode"
>
</ha-switch>
</ha-settings-row>
`} `}
`; `;
} }
@ -179,17 +234,24 @@ export class HuiCardLayoutEditor extends LitElement {
this._cardElement.config = this.config; this._cardElement.config = this.config;
this._cardElement.addEventListener("card-updated", (ev: Event) => { this._cardElement.addEventListener("card-updated", (ev: Event) => {
ev.stopPropagation(); ev.stopPropagation();
this._defaultLayoutOptions = this._updateDefaultGridOptions();
this._cardElement?.getElementLayoutOptions();
}); });
this._cardElement.load(); this._cardElement.load();
this._defaultLayoutOptions = this._cardElement.getElementLayoutOptions(); this._updateDefaultGridOptions();
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(err); console.error(err);
} }
} }
private _updateDefaultGridOptions() {
if (!this._cardElement) {
this._defaultGridOptions = undefined;
return;
}
this._defaultGridOptions = this._cardElement.getElementGridOptions();
}
protected updated(changedProps: PropertyValues<this>): void { protected updated(changedProps: PropertyValues<this>): void {
super.updated(changedProps); super.updated(changedProps);
if (this._cardElement) { if (this._cardElement) {
@ -202,6 +264,23 @@ export class HuiCardLayoutEditor extends LitElement {
} }
} }
protected willUpdate(changedProps: PropertyValues<this>): void {
super.willUpdate(changedProps);
if (changedProps.has("config")) {
const columns = this.config.grid_options?.columns;
const preciseMode = computePreciseMode(columns);
// Force precise mode if columns count is not a multiple of 3
if (!this._preciseMode && preciseMode) {
this._preciseMode = preciseMode;
}
// Reset precise mode when grid options config is reset
if (columns === undefined) {
const defaultColumns = this._defaultGridOptions?.columns;
this._preciseMode = computePreciseMode(defaultColumns);
}
}
}
private async _handleAction(ev: CustomEvent<ActionDetail>) { private async _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) { switch (ev.detail.index) {
case 0: case 0:
@ -210,68 +289,80 @@ export class HuiCardLayoutEditor extends LitElement {
case 1: case 1:
this._yamlMode = true; this._yamlMode = true;
break; break;
case 2:
this._reset();
break;
} }
} }
private async _reset() {
const newConfig = { ...this.config };
delete newConfig.layout_options;
this._yamlEditor?.setValue({});
fireEvent(this, "value-changed", { value: newConfig });
}
private _gridSizeChanged(ev: CustomEvent): void { private _gridSizeChanged(ev: CustomEvent): void {
ev.stopPropagation(); ev.stopPropagation();
const value = ev.detail.value as CardGridSize; const value = ev.detail.value as CardGridSize;
const newConfig: LovelaceCardConfig = { const gridOptions = {
...this.config, columns: value.columns,
layout_options: { rows: value.rows,
...this.config.layout_options,
grid_columns: value.columns,
grid_rows: value.rows,
},
}; };
if (newConfig.layout_options!.grid_columns === undefined) { const newOptions = this._preciseMode
delete newConfig.layout_options!.grid_columns; ? gridOptions
} : this._standardizeOptions(gridOptions);
if (newConfig.layout_options!.grid_rows === undefined) {
delete newConfig.layout_options!.grid_rows;
}
if (Object.keys(newConfig.layout_options!).length === 0) {
delete newConfig.layout_options;
}
fireEvent(this, "value-changed", { value: newConfig }); this._updateGridOptions({
...this.config.grid_options,
...newOptions,
});
} }
private _valueChanged(ev: CustomEvent): void { private _yamlChanged(ev: CustomEvent): void {
ev.stopPropagation(); ev.stopPropagation();
const options = ev.detail.value as LovelaceLayoutOptions; const options = ev.detail.value as LovelaceGridOptions;
const newConfig: LovelaceCardConfig = { this._updateGridOptions(options);
...this.config,
layout_options: options,
};
fireEvent(this, "value-changed", { value: newConfig });
} }
private _fullWidthChanged(ev): void { private _fullWidthChanged(ev): void {
ev.stopPropagation(); ev.stopPropagation();
const value = ev.target.checked; const value = ev.target.checked;
const newConfig: LovelaceCardConfig = { this._updateGridOptions({
...this.config.grid_options,
columns: value ? "full" : (this._defaultGridOptions?.min_columns ?? 1),
});
}
private _preciseModeChanged(ev): void {
ev.stopPropagation();
this._preciseMode = ev.target.checked;
if (this._preciseMode) return;
const newOptions = this._standardizeOptions(
this._simplifyOptions(this.config.grid_options ?? {})
);
if (newOptions.columns !== this.config.grid_options?.columns) {
this._updateGridOptions({
...this.config.grid_options,
columns: newOptions.columns,
});
}
}
private _updateGridOptions(options: LovelaceGridOptions): void {
const value: LovelaceCardConfig = {
...this.config, ...this.config,
layout_options: { grid_options: {
...this.config.layout_options, ...options,
grid_columns: value
? "full"
: (this._defaultLayoutOptions?.grid_min_columns ?? 1),
}, },
}; };
fireEvent(this, "value-changed", { value: newConfig }); if (value.grid_options) {
for (const [k, v] of Object.entries(value.grid_options)) {
if (v === undefined) {
delete value.grid_options[k];
}
}
if (Object.keys(value.grid_options).length === 0) {
delete value.grid_options;
}
}
if (value.layout_options) {
delete value.layout_options;
}
fireEvent(this, "value-changed", { value });
} }
static styles = [ static styles = [

View File

@ -4,5 +4,6 @@ export const baseLovelaceCardConfig = object({
type: string(), type: string(),
view_layout: any(), view_layout: any(),
layout_options: any(), layout_options: any(),
grid_options: any(),
visibility: any(), visibility: any(),
}); });

View File

@ -108,9 +108,9 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
(_cardConfig, idx) => { (_cardConfig, idx) => {
const card = this.cards![idx]; const card = this.cards![idx];
card.layout = "grid"; card.layout = "grid";
const layoutOptions = card.getLayoutOptions(); const gridOptions = card.getGridOptions();
const { rows, columns } = computeCardGridSize(layoutOptions); const { rows, columns } = computeCardGridSize(gridOptions);
const cardPath: LovelaceCardPath = [ const cardPath: LovelaceCardPath = [
this.viewIndex!, this.viewIndex!,
@ -125,7 +125,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
"--row-size": typeof rows === "number" ? rows : undefined, "--row-size": typeof rows === "number" ? rows : undefined,
})} })}
class="card ${classMap({ class="card ${classMap({
"fit-rows": typeof layoutOptions?.grid_rows === "number", "fit-rows": typeof rows === "number",
"full-width": columns === "full", "full-width": columns === "full",
})}" })}"
.sortableData=${cardPath} .sortableData=${cardPath}
@ -211,7 +211,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
haStyle, haStyle,
css` css`
:host { :host {
--base-column-count: 4; --base-column-count: 12;
--row-gap: var(--ha-section-grid-row-gap, 8px); --row-gap: var(--ha-section-grid-row-gap, 8px);
--column-gap: var(--ha-section-grid-column-gap, 8px); --column-gap: var(--ha-section-grid-column-gap, 8px);
--row-height: var(--ha-section-grid-row-height, 56px); --row-height: var(--ha-section-grid-row-height, 56px);
@ -281,7 +281,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
position: relative; position: relative;
outline: none; outline: none;
grid-row: span 1; grid-row: span 1;
grid-column: span 1; grid-column: span 3;
background: none; background: none;
cursor: pointer; cursor: pointer;
border-radius: var(--ha-card-border-radius, 12px); border-radius: var(--ha-card-border-radius, 12px);

View File

@ -53,12 +53,23 @@ export type LovelaceLayoutOptions = {
grid_max_rows?: number; grid_max_rows?: number;
}; };
export type LovelaceGridOptions = {
columns?: number | "full";
rows?: number | "auto";
max_columns?: number;
min_columns?: number;
min_rows?: number;
max_rows?: number;
};
export interface LovelaceCard extends HTMLElement { export interface LovelaceCard extends HTMLElement {
hass?: HomeAssistant; hass?: HomeAssistant;
preview?: boolean; preview?: boolean;
layout?: string; layout?: string;
getCardSize(): number | Promise<number>; getCardSize(): number | Promise<number>;
/** @deprecated Use `getGridOptions` instead */
getLayoutOptions?(): LovelaceLayoutOptions; getLayoutOptions?(): LovelaceLayoutOptions;
getGridOptions?(): LovelaceGridOptions;
setConfig(config: LovelaceCardConfig): void; setConfig(config: LovelaceCardConfig): void;
} }

View File

@ -5673,7 +5673,9 @@
}, },
"layout": { "layout": {
"full_width": "Full width card", "full_width": "Full width card",
"full_width_helper": "Take up the full width of the section whatever its size" "full_width_helper": "Take up the full width of the section whatever its size",
"precise_mode": "Precise mode",
"precise_mode_helper": "Change the card width with more precision"
} }
}, },
"edit_badge": { "edit_badge": {