mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
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:
parent
29d9b61319
commit
c0ca7e425e
@ -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}
|
||||||
|
@ -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[];
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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 = [
|
||||||
|
@ -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(),
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user