mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Improve grid size editor (#22697)
* Use table instead of grid * Add animation * Change size * Simplify precideMode logic * Add tooltip and improve slider style * Improve size * Back to default instead of min * Limit number of cells for the grid when more than 24 cells
This commit is contained in:
parent
1990b8fa84
commit
23b55484c3
@ -2,9 +2,7 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
|
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
|
|
||||||
import { mdiRestore } from "@mdi/js";
|
import { mdiRestore } from "@mdi/js";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { conditionalClamp } from "../common/number/clamp";
|
import { conditionalClamp } from "../common/number/clamp";
|
||||||
@ -20,7 +18,7 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public rows = 8;
|
@property({ attribute: false }) public rows = 8;
|
||||||
|
|
||||||
@property({ attribute: false }) public columns = 4;
|
@property({ attribute: false }) public columns = 12;
|
||||||
|
|
||||||
@property({ attribute: false }) public rowMin?: number;
|
@property({ attribute: false }) public rowMin?: number;
|
||||||
|
|
||||||
@ -32,6 +30,8 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public isDefault?: boolean;
|
@property({ attribute: false }) public isDefault?: boolean;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public step: number = 1;
|
||||||
|
|
||||||
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
|
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
|
||||||
|
|
||||||
protected willUpdate(changedProperties) {
|
protected willUpdate(changedProperties) {
|
||||||
@ -51,8 +51,9 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
|
|
||||||
const rowMin = this.rowMin ?? 1;
|
const rowMin = this.rowMin ?? 1;
|
||||||
const rowMax = this.rowMax ?? this.rows;
|
const rowMax = this.rowMax ?? this.rows;
|
||||||
const columnMin = this.columnMin ?? 1;
|
const columnMin = Math.ceil((this.columnMin ?? 1) / this.step) * this.step;
|
||||||
const columnMax = this.columnMax ?? this.columns;
|
const columnMax =
|
||||||
|
Math.ceil((this.columnMax ?? this.columns) / this.step) * this.step;
|
||||||
const rowValue = autoHeight ? rowMin : this._localValue?.rows;
|
const rowValue = autoHeight ? rowMin : this._localValue?.rows;
|
||||||
const columnValue = this._localValue?.columns;
|
const columnValue = this._localValue?.columns;
|
||||||
|
|
||||||
@ -67,9 +68,11 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
.max=${columnMax}
|
.max=${columnMax}
|
||||||
.range=${this.columns}
|
.range=${this.columns}
|
||||||
.value=${fullWidth ? this.columns : this.value?.columns}
|
.value=${fullWidth ? this.columns : this.value?.columns}
|
||||||
|
.step=${this.step}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@slider-moved=${this._sliderMoved}
|
@slider-moved=${this._sliderMoved}
|
||||||
.disabled=${disabledColumns}
|
.disabled=${disabledColumns}
|
||||||
|
tooltip-mode="always"
|
||||||
></ha-grid-layout-slider>
|
></ha-grid-layout-slider>
|
||||||
|
|
||||||
<ha-grid-layout-slider
|
<ha-grid-layout-slider
|
||||||
@ -85,6 +88,7 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@slider-moved=${this._sliderMoved}
|
@slider-moved=${this._sliderMoved}
|
||||||
.disabled=${disabledRows}
|
.disabled=${disabledRows}
|
||||||
|
tooltip-mode="always"
|
||||||
></ha-grid-layout-slider>
|
></ha-grid-layout-slider>
|
||||||
${!this.isDefault
|
${!this.isDefault
|
||||||
? html`
|
? html`
|
||||||
@ -102,34 +106,44 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<div
|
<div class="preview">
|
||||||
class="preview ${classMap({ "full-width": fullWidth })}"
|
<table>
|
||||||
style=${styleMap({
|
${Array(this.rows)
|
||||||
"--total-rows": this.rows,
|
|
||||||
"--total-columns": this.columns,
|
|
||||||
"--rows": rowValue,
|
|
||||||
"--columns": fullWidth ? this.columns : columnValue,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
${Array(this.rows * this.columns)
|
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map((_, index) => {
|
.map((_, index) => {
|
||||||
const row = Math.floor(index / this.columns) + 1;
|
const row = index + 1;
|
||||||
const column = (index % this.columns) + 1;
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<tr>
|
||||||
class="cell"
|
${Array(this.columns)
|
||||||
|
.fill(0)
|
||||||
|
.map((__, columnIndex) => {
|
||||||
|
const column = columnIndex + 1;
|
||||||
|
if (
|
||||||
|
column % this.step !== 0 ||
|
||||||
|
(this.columns > 24 && column % 3 !== 0)
|
||||||
|
) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<td
|
||||||
data-row=${row}
|
data-row=${row}
|
||||||
data-column=${column}
|
data-column=${column}
|
||||||
@click=${this._cellClick}
|
@click=${this._cellClick}
|
||||||
></div>
|
></td>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</div>
|
</tr>
|
||||||
<div class="selected">
|
`;
|
||||||
<div class="cell"></div>
|
})}
|
||||||
</div>
|
</table>
|
||||||
|
<div
|
||||||
|
class="preview-card"
|
||||||
|
style=${styleMap({
|
||||||
|
"--rows": rowValue,
|
||||||
|
"--columns": fullWidth ? this.columns : columnValue,
|
||||||
|
"--total-columns": this.columns,
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -223,42 +237,40 @@ export class HaGridSizeEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
.reset {
|
.reset {
|
||||||
grid-area: reset;
|
grid-area: reset;
|
||||||
|
--mdc-icon-button-size: 36px;
|
||||||
}
|
}
|
||||||
.preview {
|
.preview {
|
||||||
position: relative;
|
position: relative;
|
||||||
grid-area: preview;
|
grid-area: preview;
|
||||||
}
|
}
|
||||||
.preview > div {
|
.preview table,
|
||||||
position: relative;
|
.preview tr,
|
||||||
display: grid;
|
.preview td {
|
||||||
grid-template-columns: repeat(var(--total-columns), 1fr);
|
border: 2px dotted var(--divider-color);
|
||||||
grid-template-rows: repeat(var(--total-rows), 25px);
|
border-collapse: collapse;
|
||||||
gap: 4px;
|
|
||||||
}
|
}
|
||||||
.preview .cell {
|
.preview table {
|
||||||
background-color: var(--disabled-color);
|
|
||||||
grid-column: span 1;
|
|
||||||
grid-row: span 1;
|
|
||||||
border-radius: 4px;
|
|
||||||
opacity: 0.2;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.preview .selected {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.selected .cell {
|
.preview tr {
|
||||||
background-color: var(--primary-color);
|
height: 30px;
|
||||||
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 {
|
.preview td {
|
||||||
grid-column: 1 / -1;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.preview-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
opacity: 0.3;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: calc(var(--rows, 1) * 30px);
|
||||||
|
width: calc(var(--columns, 1) * 100% / var(--total-columns, 12));
|
||||||
|
pointer-events: none;
|
||||||
|
transition:
|
||||||
|
width ease-in-out 180ms,
|
||||||
|
height ease-in-out 180ms;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -3,16 +3,11 @@ import type { LovelaceGridOptions, LovelaceLayoutOptions } from "../types";
|
|||||||
|
|
||||||
export const GRID_COLUMN_MULTIPLIER = 3;
|
export const GRID_COLUMN_MULTIPLIER = 3;
|
||||||
|
|
||||||
export const multiplyBy = <T extends number | string | undefined>(
|
const multiplyBy = <T extends number | string | undefined>(
|
||||||
value: T,
|
value: T,
|
||||||
multiplier: number
|
multiplier: number
|
||||||
): T => (typeof value === "number" ? ((value * multiplier) as T) : value);
|
): 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 = (
|
export const migrateLayoutToGridOptions = (
|
||||||
options: LovelaceLayoutOptions
|
options: LovelaceLayoutOptions
|
||||||
): LovelaceGridOptions => {
|
): LovelaceGridOptions => {
|
||||||
@ -42,6 +37,9 @@ export type CardGridSize = {
|
|||||||
columns: number | "full";
|
columns: number | "full";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isPreciseMode = (options: LovelaceGridOptions) =>
|
||||||
|
typeof options.columns === "number" && options.columns % 3 !== 0;
|
||||||
|
|
||||||
export const computeCardGridSize = (
|
export const computeCardGridSize = (
|
||||||
options: LovelaceGridOptions
|
options: LovelaceGridOptions
|
||||||
): CardGridSize => {
|
): CardGridSize => {
|
||||||
|
@ -23,6 +23,8 @@ const A11Y_KEY_CODES = new Set([
|
|||||||
"End",
|
"End",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
type TooltipMode = "never" | "always" | "interaction";
|
||||||
|
|
||||||
@customElement("ha-grid-layout-slider")
|
@customElement("ha-grid-layout-slider")
|
||||||
export class HaGridLayoutSlider extends LitElement {
|
export class HaGridLayoutSlider extends LitElement {
|
||||||
@property({ type: Boolean, reflect: true })
|
@property({ type: Boolean, reflect: true })
|
||||||
@ -34,6 +36,9 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
@property({ attribute: "touch-action" })
|
@property({ attribute: "touch-action" })
|
||||||
public touchAction?: string;
|
public touchAction?: string;
|
||||||
|
|
||||||
|
@property({ attribute: "tooltip-mode" })
|
||||||
|
public tooltipMode: TooltipMode = "interaction";
|
||||||
|
|
||||||
@property({ type: Number })
|
@property({ type: Number })
|
||||||
public value?: number;
|
public value?: number;
|
||||||
|
|
||||||
@ -52,6 +57,9 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
@state()
|
@state()
|
||||||
public pressed = false;
|
public pressed = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
public tooltipVisible = false;
|
||||||
|
|
||||||
private _mc?: HammerManager;
|
private _mc?: HammerManager;
|
||||||
|
|
||||||
private get _range() {
|
private get _range() {
|
||||||
@ -135,11 +143,13 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
this._mc.on("panstart", () => {
|
this._mc.on("panstart", () => {
|
||||||
if (this.disabled) return;
|
if (this.disabled) return;
|
||||||
this.pressed = true;
|
this.pressed = true;
|
||||||
|
this._showTooltip();
|
||||||
savedValue = this.value;
|
savedValue = this.value;
|
||||||
});
|
});
|
||||||
this._mc.on("pancancel", () => {
|
this._mc.on("pancancel", () => {
|
||||||
if (this.disabled) return;
|
if (this.disabled) return;
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
|
this._hideTooltip();
|
||||||
this.value = savedValue;
|
this.value = savedValue;
|
||||||
});
|
});
|
||||||
this._mc.on("panmove", (e) => {
|
this._mc.on("panmove", (e) => {
|
||||||
@ -152,6 +162,7 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
this._mc.on("panend", (e) => {
|
this._mc.on("panend", (e) => {
|
||||||
if (this.disabled) return;
|
if (this.disabled) return;
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
|
this._hideTooltip();
|
||||||
const percentage = this._getPercentageFromEvent(e);
|
const percentage = this._getPercentageFromEvent(e);
|
||||||
const value = this._percentageToValue(percentage);
|
const value = this._percentageToValue(percentage);
|
||||||
this.value = this._steppedValue(this._boundedValue(value));
|
this.value = this._steppedValue(this._boundedValue(value));
|
||||||
@ -223,6 +234,23 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
fireEvent(this, "value-changed", { value: this.value });
|
fireEvent(this, "value-changed", { value: this.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _tooltipTimeout?: number;
|
||||||
|
|
||||||
|
_showTooltip() {
|
||||||
|
if (this._tooltipTimeout != null) window.clearTimeout(this._tooltipTimeout);
|
||||||
|
this.tooltipVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideTooltip(delay?: number) {
|
||||||
|
if (!delay) {
|
||||||
|
this.tooltipVisible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._tooltipTimeout = window.setTimeout(() => {
|
||||||
|
this.tooltipVisible = false;
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
private _getPercentageFromEvent = (e: HammerInput) => {
|
private _getPercentageFromEvent = (e: HammerInput) => {
|
||||||
if (this.vertical) {
|
if (this.vertical) {
|
||||||
const y = e.center.y;
|
const y = e.center.y;
|
||||||
@ -236,6 +264,30 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
return Math.max(Math.min(1, (x - offset) / total), 0);
|
return Math.max(Math.min(1, (x - offset) / total), 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _renderTooltip() {
|
||||||
|
if (this.tooltipMode === "never") return nothing;
|
||||||
|
|
||||||
|
const position = this.vertical ? "left" : "top";
|
||||||
|
|
||||||
|
const visible =
|
||||||
|
this.tooltipMode === "always" ||
|
||||||
|
(this.tooltipVisible && this.tooltipMode === "interaction");
|
||||||
|
|
||||||
|
const value = this._boundedValue(this._steppedValue(this.value ?? 0));
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="tooltip ${classMap({
|
||||||
|
visible,
|
||||||
|
[position]: true,
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
${value}
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@ -257,9 +309,29 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
})}
|
})}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${Array(this._range / this.step)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => {
|
||||||
|
const percentage = i / (this._range / this.step);
|
||||||
|
const disabled =
|
||||||
|
this.min >= i * this.step || i * this.step > this.max;
|
||||||
|
if (disabled) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="dot"
|
||||||
|
style=${styleMap({
|
||||||
|
"--value": `${percentage}`,
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
${this.value !== undefined
|
${this.value !== undefined
|
||||||
? html`<div class="handle"></div>`
|
? html`<div class="handle"></div>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${this._renderTooltip()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -269,7 +341,7 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
--grid-layout-slider: 48px;
|
--grid-layout-slider: 36px;
|
||||||
height: var(--grid-layout-slider);
|
height: var(--grid-layout-slider);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -297,6 +369,7 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
}
|
}
|
||||||
.slider * {
|
.slider * {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.track {
|
.track {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -315,12 +388,11 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: var(--disabled-color);
|
background: var(--disabled-color);
|
||||||
opacity: 0.2;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
.active {
|
.active {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: grey;
|
background: var(--primary-color);
|
||||||
opacity: 0.7;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
right: calc(var(--max) * 100%);
|
right: calc(var(--max) * 100%);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -351,6 +423,27 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.dot {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0.6;
|
||||||
|
margin: auto;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
background: var(--card-background-color);
|
||||||
|
left: calc(var(--value, 0%) * 100%);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
:host([vertical]) .dot {
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: inherit;
|
||||||
|
top: calc(var(--value, 0%) * 100%);
|
||||||
|
}
|
||||||
.handle::after {
|
.handle::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@ -358,7 +451,7 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background: grey;
|
background: var(--primary-color);
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
:host([vertical]) .handle::after {
|
:host([vertical]) .handle::after {
|
||||||
@ -374,9 +467,88 @@ export class HaGridLayoutSlider extends LitElement {
|
|||||||
:host(:disabled) .active {
|
:host(:disabled) .active {
|
||||||
background: var(--disabled-color);
|
background: var(--disabled-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--clear-background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: var(--control-slider-tooltip-font-size);
|
||||||
|
border-radius: 0.8em;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
opacity: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
transition:
|
||||||
|
opacity 180ms ease-in-out,
|
||||||
|
left 180ms ease-in-out,
|
||||||
|
bottom 180ms ease-in-out;
|
||||||
|
--handle-spacing: calc(2 * var(--handle-margin) + var(--handle-size));
|
||||||
|
--slider-tooltip-margin: 0px;
|
||||||
|
--slider-tooltip-range: 100%;
|
||||||
|
--slider-tooltip-offset: 0px;
|
||||||
|
--slider-tooltip-position: calc(
|
||||||
|
min(
|
||||||
|
max(
|
||||||
|
var(--value) * var(--slider-tooltip-range) +
|
||||||
|
var(--slider-tooltip-offset),
|
||||||
|
0%
|
||||||
|
),
|
||||||
|
100%
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.tooltip.start {
|
||||||
|
--slider-tooltip-offset: calc(-0.5 * (var(--handle-spacing)));
|
||||||
|
}
|
||||||
|
.tooltip.end {
|
||||||
|
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||||
|
}
|
||||||
|
.tooltip.cursor {
|
||||||
|
--slider-tooltip-range: calc(100% - var(--handle-spacing));
|
||||||
|
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||||
|
}
|
||||||
|
.tooltip.show-handle {
|
||||||
|
--slider-tooltip-range: calc(100% - var(--handle-spacing));
|
||||||
|
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||||
|
}
|
||||||
|
.tooltip.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.tooltip.top {
|
||||||
|
transform: translate3d(-50%, -100%, 0);
|
||||||
|
top: var(--slider-tooltip-margin);
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
.tooltip.bottom {
|
||||||
|
transform: translate3d(-50%, 100%, 0);
|
||||||
|
bottom: var(--slider-tooltip-margin);
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
.tooltip.left {
|
||||||
|
transform: translate3d(-100%, -50%, 0);
|
||||||
|
top: 50%;
|
||||||
|
left: var(--slider-tooltip-margin);
|
||||||
|
}
|
||||||
|
.tooltip.right {
|
||||||
|
transform: translate3d(100%, -50%, 0);
|
||||||
|
top: 50%;
|
||||||
|
right: var(--slider-tooltip-margin);
|
||||||
|
}
|
||||||
|
:host(:not([vertical])) .tooltip.top,
|
||||||
|
:host(:not([vertical])) .tooltip.bottom {
|
||||||
|
left: var(--slider-tooltip-position);
|
||||||
|
}
|
||||||
|
:host([vertical]) .tooltip.right,
|
||||||
|
:host([vertical]) .tooltip.left {
|
||||||
|
top: var(--slider-tooltip-position);
|
||||||
|
}
|
||||||
|
|
||||||
.pressed .handle {
|
.pressed .handle {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
.pressed .tooltip {
|
||||||
|
transition: opacity 180ms ease-in-out;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,16 +26,12 @@ 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 {
|
import {
|
||||||
computeCardGridSize,
|
computeCardGridSize,
|
||||||
divideBy,
|
|
||||||
GRID_COLUMN_MULTIPLIER,
|
GRID_COLUMN_MULTIPLIER,
|
||||||
|
isPreciseMode,
|
||||||
migrateLayoutToGridOptions,
|
migrateLayoutToGridOptions,
|
||||||
multiplyBy,
|
|
||||||
} from "../../common/compute-card-grid-size";
|
} from "../../common/compute-card-grid-size";
|
||||||
import type { LovelaceGridOptions } from "../../types";
|
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 {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -63,22 +59,6 @@ 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?: LovelaceGridOptions) =>
|
(options?: LovelaceGridOptions) =>
|
||||||
options?.columns === undefined && options?.rows === undefined
|
options?.columns === undefined && options?.rows === undefined
|
||||||
@ -101,14 +81,11 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
this._defaultGridOptions
|
this._defaultGridOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
const gridOptions = this._preciseMode
|
const gridOptions = options;
|
||||||
? options
|
|
||||||
: this._simplifyOptions(options);
|
|
||||||
const gridValue = this._computeCardGridSize(gridOptions);
|
const gridValue = this._computeCardGridSize(gridOptions);
|
||||||
|
|
||||||
const columnSpan = this.sectionConfig.column_span ?? 1;
|
const columnSpan = this.sectionConfig.column_span ?? 1;
|
||||||
const gridTotalColumns =
|
const gridTotalColumns = 12 * columnSpan;
|
||||||
(12 * columnSpan) / (this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@ -173,7 +150,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<ha-grid-size-picker
|
<ha-grid-size-picker
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
"max-width": `${(this.sectionConfig.column_span ?? 1) * 200 + 50}px`,
|
"max-width": `${(this.sectionConfig.column_span ?? 1) * 250 + 40}px`,
|
||||||
})}
|
})}
|
||||||
.columns=${gridTotalColumns}
|
.columns=${gridTotalColumns}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -184,6 +161,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
.rowMax=${gridOptions.max_rows}
|
.rowMax=${gridOptions.max_rows}
|
||||||
.columnMin=${gridOptions.min_columns}
|
.columnMin=${gridOptions.min_columns}
|
||||||
.columnMax=${gridOptions.max_columns}
|
.columnMax=${gridOptions.max_columns}
|
||||||
|
.step=${this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER}
|
||||||
></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">
|
||||||
@ -267,17 +245,21 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
protected willUpdate(changedProps: PropertyValues<this>): void {
|
protected willUpdate(changedProps: PropertyValues<this>): void {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
if (changedProps.has("config")) {
|
if (changedProps.has("config")) {
|
||||||
const columns = this.config.grid_options?.columns;
|
const options = this.config.grid_options;
|
||||||
const preciseMode = computePreciseMode(columns);
|
|
||||||
|
// Reset precise mode when grid options config is reset
|
||||||
|
if (!options) {
|
||||||
|
this._preciseMode = this._defaultGridOptions
|
||||||
|
? isPreciseMode(this._defaultGridOptions)
|
||||||
|
: false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Force precise mode if columns count is not a multiple of 3
|
// Force precise mode if columns count is not a multiple of 3
|
||||||
|
const preciseMode = isPreciseMode(options);
|
||||||
if (!this._preciseMode && preciseMode) {
|
if (!this._preciseMode && preciseMode) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,18 +278,10 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const value = ev.detail.value as CardGridSize;
|
const value = ev.detail.value as CardGridSize;
|
||||||
|
|
||||||
const gridOptions = {
|
|
||||||
columns: value.columns,
|
|
||||||
rows: value.rows,
|
|
||||||
};
|
|
||||||
|
|
||||||
const newOptions = this._preciseMode
|
|
||||||
? gridOptions
|
|
||||||
: this._standardizeOptions(gridOptions);
|
|
||||||
|
|
||||||
this._updateGridOptions({
|
this._updateGridOptions({
|
||||||
...this.config.grid_options,
|
...this.config.grid_options,
|
||||||
...newOptions,
|
columns: value.columns,
|
||||||
|
rows: value.rows,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +296,7 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
const value = ev.target.checked;
|
const value = ev.target.checked;
|
||||||
this._updateGridOptions({
|
this._updateGridOptions({
|
||||||
...this.config.grid_options,
|
...this.config.grid_options,
|
||||||
columns: value ? "full" : (this._defaultGridOptions?.min_columns ?? 1),
|
columns: value ? "full" : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,13 +305,14 @@ export class HuiCardLayoutEditor extends LitElement {
|
|||||||
this._preciseMode = ev.target.checked;
|
this._preciseMode = ev.target.checked;
|
||||||
if (this._preciseMode) return;
|
if (this._preciseMode) return;
|
||||||
|
|
||||||
const newOptions = this._standardizeOptions(
|
const columns = this.config.grid_options?.columns;
|
||||||
this._simplifyOptions(this.config.grid_options ?? {})
|
|
||||||
);
|
if (typeof columns === "number" && columns % GRID_COLUMN_MULTIPLIER !== 0) {
|
||||||
if (newOptions.columns !== this.config.grid_options?.columns) {
|
const newColumns =
|
||||||
|
Math.ceil(columns / GRID_COLUMN_MULTIPLIER) * GRID_COLUMN_MULTIPLIER;
|
||||||
this._updateGridOptions({
|
this._updateGridOptions({
|
||||||
...this.config.grid_options,
|
...this.config.grid_options,
|
||||||
columns: newOptions.columns,
|
columns: newColumns,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user