mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-22 15:49:26 +00:00
Compare commits
44 Commits
20211201.0
...
lit-grid-l
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2fef57f0b2 | ||
![]() |
9b279632f8 | ||
![]() |
480e781364 | ||
![]() |
adc10ff0c6 | ||
![]() |
57a718e409 | ||
![]() |
23462bacfa | ||
![]() |
d99f8b0da8 | ||
![]() |
991d1f0997 | ||
![]() |
54e99357b5 | ||
![]() |
d263b5550a | ||
![]() |
a2b6ce3437 | ||
![]() |
9458f7ae62 | ||
![]() |
4d2ae36b0d | ||
![]() |
ebf0050e81 | ||
![]() |
3d0f492d62 | ||
![]() |
f5d3237b06 | ||
![]() |
07671ba1d4 | ||
![]() |
354d74ce1d | ||
![]() |
e892d14af0 | ||
![]() |
8d034fb7e7 | ||
![]() |
249456e6a0 | ||
![]() |
d3a6f31310 | ||
![]() |
4c31aead8b | ||
![]() |
3caf91c5d3 | ||
![]() |
9c3e754d53 | ||
![]() |
f48765c2b6 | ||
![]() |
236f26e652 | ||
![]() |
0fed96aba3 | ||
![]() |
70b77833f1 | ||
![]() |
307694a820 | ||
![]() |
402391c3e1 | ||
![]() |
81b5866e4d | ||
![]() |
09985bf5f7 | ||
![]() |
71d12a9e7a | ||
![]() |
5fd733b6c9 | ||
![]() |
bc3f827b4a | ||
![]() |
a807a182e4 | ||
![]() |
0a1fb843ca | ||
![]() |
fceb1568f5 | ||
![]() |
74e93bbefc | ||
![]() |
5c0f4b564b | ||
![]() |
76734f7a0b | ||
![]() |
440d10e4cd | ||
![]() |
a901072695 |
@@ -35,6 +35,9 @@ const createWebpackConfig = ({
|
|||||||
loader: "babel-loader",
|
loader: "babel-loader",
|
||||||
options: bundle.babelOptions({ latestBuild }),
|
options: bundle.babelOptions({ latestBuild }),
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
fullySpecified: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
|
@@ -105,6 +105,7 @@
|
|||||||
"leaflet": "^1.4.0",
|
"leaflet": "^1.4.0",
|
||||||
"leaflet-draw": "^1.0.4",
|
"leaflet-draw": "^1.0.4",
|
||||||
"lit-element": "^2.4.0",
|
"lit-element": "^2.4.0",
|
||||||
|
"lit-grid-layout": "^1.1.11",
|
||||||
"lit-html": "^1.3.0",
|
"lit-html": "^1.3.0",
|
||||||
"lit-virtualizer": "^0.4.2",
|
"lit-virtualizer": "^0.4.2",
|
||||||
"marked": "^1.1.1",
|
"marked": "^1.1.1",
|
||||||
@@ -121,6 +122,7 @@
|
|||||||
"superstruct": "^0.10.12",
|
"superstruct": "^0.10.12",
|
||||||
"tinykeys": "^1.1.1",
|
"tinykeys": "^1.1.1",
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
|
"uuid": "^8.3.0",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue2-daterange-picker": "^0.5.1",
|
"vue2-daterange-picker": "^0.5.1",
|
||||||
"web-animations-js": "^2.3.2",
|
"web-animations-js": "^2.3.2",
|
||||||
|
@@ -9,6 +9,7 @@ const ALWAYS_LOADED_LAYOUTS = new Set(["masonry"]);
|
|||||||
|
|
||||||
const LAZY_LOAD_LAYOUTS = {
|
const LAZY_LOAD_LAYOUTS = {
|
||||||
panel: () => import("../views/hui-panel-view"),
|
panel: () => import("../views/hui-panel-view"),
|
||||||
|
grid: () => import("../views/hui-grid-view"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createViewElement = (
|
export const createViewElement = (
|
||||||
|
@@ -13,6 +13,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import {
|
import {
|
||||||
|
any,
|
||||||
array,
|
array,
|
||||||
assert,
|
assert,
|
||||||
boolean,
|
boolean,
|
||||||
@@ -62,6 +63,7 @@ const cardConfigStruct = object({
|
|||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
show_header_toggle: optional(boolean()),
|
show_header_toggle: optional(boolean()),
|
||||||
state_color: optional(boolean()),
|
state_color: optional(boolean()),
|
||||||
|
layout: optional(any()),
|
||||||
entities: array(entitiesConfigStruct),
|
entities: array(entitiesConfigStruct),
|
||||||
header: optional(headerFooterConfigStructs),
|
header: optional(headerFooterConfigStructs),
|
||||||
footer: optional(headerFooterConfigStructs),
|
footer: optional(headerFooterConfigStructs),
|
||||||
|
@@ -36,6 +36,7 @@ export interface LovelaceCard extends HTMLElement {
|
|||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
isPanel?: boolean;
|
isPanel?: boolean;
|
||||||
editMode?: boolean;
|
editMode?: boolean;
|
||||||
|
layout?: any;
|
||||||
getCardSize(): number | Promise<number>;
|
getCardSize(): number | Promise<number>;
|
||||||
setConfig(config: LovelaceCardConfig): void;
|
setConfig(config: LovelaceCardConfig): void;
|
||||||
}
|
}
|
||||||
|
3
src/panels/lovelace/views/grid/grid-view-editable.ts
Normal file
3
src/panels/lovelace/views/grid/grid-view-editable.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// hui-grid-view dependencies for when in edit mode.
|
||||||
|
import "@material/mwc-fab";
|
||||||
|
import "./hui-grid-card-options";
|
195
src/panels/lovelace/views/grid/hui-grid-card-options.ts
Normal file
195
src/panels/lovelace/views/grid/hui-grid-card-options.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import "@material/mwc-icon-button";
|
||||||
|
import {
|
||||||
|
mdiContentDuplicate,
|
||||||
|
mdiDelete,
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiPencil,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
queryAssignedNodes,
|
||||||
|
} from "lit-element";
|
||||||
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { computeCardSize } from "../../common/compute-card-size";
|
||||||
|
import { showEditCardDialog } from "../../editor/card-editor/show-edit-card-dialog";
|
||||||
|
import { confDeleteCard } from "../../editor/delete-card";
|
||||||
|
import { Lovelace, LovelaceCard } from "../../types";
|
||||||
|
|
||||||
|
@customElement("hui-grid-card-options")
|
||||||
|
export class HuiGridCardOptions extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public lovelace?: Lovelace;
|
||||||
|
|
||||||
|
@property({ type: Array }) public path?: [number, number];
|
||||||
|
|
||||||
|
@queryAssignedNodes() private _assignedNodes?: NodeListOf<LovelaceCard>;
|
||||||
|
|
||||||
|
public getCardSize() {
|
||||||
|
return this._assignedNodes ? computeCardSize(this._assignedNodes[0]) : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<slot></slot>
|
||||||
|
<div class="parent-card-actions">
|
||||||
|
<div class="overlay"></div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-icon-button
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.edit"
|
||||||
|
)}
|
||||||
|
@click=${this._editCard}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-icon-button
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.duplicate"
|
||||||
|
)}
|
||||||
|
@click=${this._duplicateCard}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiContentDuplicate}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-icon-button
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.delete"
|
||||||
|
)}
|
||||||
|
@click=${this._deleteCard}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<div>
|
||||||
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
|
<mwc-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
aria-label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.options"
|
||||||
|
)}
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.options"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.move"
|
||||||
|
)}
|
||||||
|
</mwc-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
slot {
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
transition: all 0.25s;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-card-actions:hover .overlay {
|
||||||
|
outline: 2px solid var(--primary-color);
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
/* background-color: grey; */
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-card-actions {
|
||||||
|
transition: all 0.25s;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-card-actions:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 24px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions > * {
|
||||||
|
margin: 0 4px;
|
||||||
|
border-radius: 24px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-list-item {
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-list-item.delete-item {
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _duplicateCard(): void {
|
||||||
|
const path = this.path!;
|
||||||
|
const cardConfig = this.lovelace!.config.views[path[0]].cards![path[1]];
|
||||||
|
showEditCardDialog(this, {
|
||||||
|
lovelaceConfig: this.lovelace!.config,
|
||||||
|
cardConfig,
|
||||||
|
saveConfig: this.lovelace!.saveConfig,
|
||||||
|
path: [path[0]],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _editCard(): void {
|
||||||
|
showEditCardDialog(this, {
|
||||||
|
lovelaceConfig: this.lovelace!.config,
|
||||||
|
saveConfig: this.lovelace!.saveConfig,
|
||||||
|
path: this.path!,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deleteCard(): void {
|
||||||
|
confDeleteCard(this, this.hass!, this.lovelace!, this.path!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-grid-card-options": HuiGridCardOptions;
|
||||||
|
}
|
||||||
|
}
|
363
src/panels/lovelace/views/hui-grid-view.ts
Normal file
363
src/panels/lovelace/views/hui-grid-view.ts
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
import "@material/mwc-fab/mwc-fab";
|
||||||
|
import { mdiPlus, mdiResizeBottomRight } from "@mdi/js";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "lit-grid-layout";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
|
import { nextRender } from "../../../common/util/render-status";
|
||||||
|
import "../../../components/entity/ha-state-label-badge";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import type {
|
||||||
|
LovelaceViewConfig,
|
||||||
|
LovelaceViewElement,
|
||||||
|
} from "../../../data/lovelace";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import { computeCardSize } from "../common/compute-card-size";
|
||||||
|
import { HuiCardOptions } from "../components/hui-card-options";
|
||||||
|
import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog";
|
||||||
|
import { replaceView } from "../editor/config-util";
|
||||||
|
import type { Lovelace, LovelaceBadge, LovelaceCard } from "../types";
|
||||||
|
import { HuiGridCardOptions } from "./grid/hui-grid-card-options";
|
||||||
|
|
||||||
|
let editCodeLoaded = false;
|
||||||
|
const mediaQueryColumns = [2, 6, 9, 12];
|
||||||
|
|
||||||
|
interface LovelaceGridCard extends LovelaceCard, HuiGridCardOptions {
|
||||||
|
key: string;
|
||||||
|
grid?: {
|
||||||
|
key: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
posX: number;
|
||||||
|
posY: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const RESIZE_HANDLE = document.createElement("div") as HTMLElement;
|
||||||
|
RESIZE_HANDLE.style.cssText =
|
||||||
|
"width: 100%; height: 100%; cursor: se-resize; fill: var(--primary-text-color)";
|
||||||
|
RESIZE_HANDLE.innerHTML = `
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
focusable="false"
|
||||||
|
>
|
||||||
|
<g><path d=${mdiResizeBottomRight}></path></g>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
|
@customElement("hui-grid-view")
|
||||||
|
export class GridView extends LitElement implements LovelaceViewElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public lovelace?: Lovelace;
|
||||||
|
|
||||||
|
@property({ type: Number }) public index?: number;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public cards: Array<LovelaceGridCard> = [];
|
||||||
|
|
||||||
|
@property({ attribute: false }) public badges: LovelaceBadge[] = [];
|
||||||
|
|
||||||
|
@internalProperty() private _columns?: number;
|
||||||
|
|
||||||
|
@internalProperty() private _layout?: Array<{
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
posX: number;
|
||||||
|
posY: number;
|
||||||
|
key: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
@internalProperty() public _cards: {
|
||||||
|
[key: string]: LovelaceCard | HuiCardOptions;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
private _config?: LovelaceViewConfig;
|
||||||
|
|
||||||
|
private _createColumnsIteration = 0;
|
||||||
|
|
||||||
|
private _mqls?: MediaQueryList[];
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
this.addEventListener("iron-resize", (ev: Event) => ev.stopPropagation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public setConfig(config: LovelaceViewConfig): void {
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
id="badges"
|
||||||
|
style=${this.badges.length > 0 ? "display: block" : "display: none"}
|
||||||
|
>
|
||||||
|
${this.badges.map((badge) => html`${badge}`)}
|
||||||
|
</div>
|
||||||
|
<lit-grid-layout
|
||||||
|
rowHeight="15"
|
||||||
|
.resizeHandle=${RESIZE_HANDLE}
|
||||||
|
.itemRenderer=${this._itemRenderer}
|
||||||
|
.layout=${this._layout}
|
||||||
|
.columns=${this._columns}
|
||||||
|
.dragDisabled=${!this.lovelace?.editMode}
|
||||||
|
.resizeDisabled=${!this.lovelace?.editMode}
|
||||||
|
@item-changed=${this._saveLayout}
|
||||||
|
></lit-grid-layout>
|
||||||
|
${this.lovelace?.editMode
|
||||||
|
? html`
|
||||||
|
<mwc-fab
|
||||||
|
class=${classMap({
|
||||||
|
rtl: computeRTL(this.hass!),
|
||||||
|
})}
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.add"
|
||||||
|
)}
|
||||||
|
@click=${this._addCard}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</mwc-fab>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
this._updateColumns = this._updateColumns.bind(this);
|
||||||
|
this._mqls = [300, 600, 900, 1200].map((width) => {
|
||||||
|
const mql = matchMedia(`(min-width: ${width}px)`);
|
||||||
|
mql.addEventListener("change", this._updateColumns);
|
||||||
|
return mql;
|
||||||
|
});
|
||||||
|
this._updateColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if (this.lovelace?.editMode && !editCodeLoaded) {
|
||||||
|
editCodeLoaded = true;
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "grid-view-editable" */ "./grid/grid-view-editable"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("hass")) {
|
||||||
|
const oldHass = changedProperties.get("hass") as HomeAssistant;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(oldHass && this.hass!.dockedSidebar !== oldHass.dockedSidebar) ||
|
||||||
|
(!oldHass && this.hass)
|
||||||
|
) {
|
||||||
|
this._updateColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.size === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldLovelace = changedProperties.get("lovelace") as
|
||||||
|
| Lovelace
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(changedProperties.has("lovelace") &&
|
||||||
|
(oldLovelace?.config !== this.lovelace?.config ||
|
||||||
|
oldLovelace?.editMode !== this.lovelace?.editMode)) ||
|
||||||
|
changedProperties.has("_columns")
|
||||||
|
) {
|
||||||
|
this._createLayout();
|
||||||
|
this._createCards();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _saveLayout(ev: CustomEvent): Promise<void> {
|
||||||
|
console.log("save");
|
||||||
|
|
||||||
|
const viewConf: LovelaceViewConfig = {
|
||||||
|
...this._config,
|
||||||
|
layout: ev.detail.layout,
|
||||||
|
};
|
||||||
|
await this.lovelace?.saveConfig(
|
||||||
|
replaceView(this.lovelace!.config, this.index, viewConf)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _itemRenderer = (key: string): TemplateResult => {
|
||||||
|
if (!this._cards) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`${this._cards[key]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
private _addCard(): void {
|
||||||
|
showCreateCardDialog(this, {
|
||||||
|
lovelaceConfig: this.lovelace!.config,
|
||||||
|
saveConfig: this.lovelace!.saveConfig,
|
||||||
|
path: [this.index!],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _createLayout() {
|
||||||
|
this._createColumnsIteration++;
|
||||||
|
const iteration = this._createColumnsIteration;
|
||||||
|
|
||||||
|
if (this._layout?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLayout: Array<{
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
posX: number;
|
||||||
|
posY: number;
|
||||||
|
key: string;
|
||||||
|
minHeight: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
let tillNextRender: Promise<unknown> | undefined;
|
||||||
|
let start: Date | undefined;
|
||||||
|
|
||||||
|
// Calculate the size of every card and determine in what column it should go
|
||||||
|
for (const [index, card] of this.cards.entries()) {
|
||||||
|
const cardConfig = this._config!.cards![index];
|
||||||
|
if (tillNextRender === undefined) {
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
tillNextRender = nextRender().then(() => {
|
||||||
|
tillNextRender = undefined;
|
||||||
|
start = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let waitProm: Promise<unknown> | undefined;
|
||||||
|
|
||||||
|
// We should work for max 16ms (60fps) before allowing a frame to render
|
||||||
|
if (start === undefined) {
|
||||||
|
// Save the time we start for this frame, no need to wait yet
|
||||||
|
start = new Date();
|
||||||
|
} else if (new Date().getTime() - start.getTime() > 16) {
|
||||||
|
// We are working too long, we will prevent a render, wait to allow for a render
|
||||||
|
waitProm = tillNextRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardSizeProm = computeCardSize(card);
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const [cardSize] = await Promise.all([cardSizeProm, waitProm]);
|
||||||
|
|
||||||
|
if (iteration !== this._createColumnsIteration) {
|
||||||
|
// An other create columns is started, abort this one
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const layout = {
|
||||||
|
width: 3,
|
||||||
|
height: cardSize,
|
||||||
|
key: uuidv4(),
|
||||||
|
minHeight: 4,
|
||||||
|
...cardConfig.layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
newLayout.push(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._layout = newLayout;
|
||||||
|
this._createCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createCards(): void {
|
||||||
|
const elements = {};
|
||||||
|
this.cards.forEach((card: LovelaceGridCard, index) => {
|
||||||
|
const layout = this._layout![index];
|
||||||
|
|
||||||
|
card.editMode = this.lovelace?.editMode;
|
||||||
|
let element = card;
|
||||||
|
|
||||||
|
if (this.lovelace?.editMode) {
|
||||||
|
const wrapper = document.createElement(
|
||||||
|
"hui-grid-card-options"
|
||||||
|
) as LovelaceGridCard;
|
||||||
|
wrapper.hass = this.hass;
|
||||||
|
wrapper.lovelace = this.lovelace;
|
||||||
|
wrapper.path = [this.index!, index];
|
||||||
|
wrapper.appendChild(card);
|
||||||
|
element = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
elements[layout.key] = element;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._cards = elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updateColumns() {
|
||||||
|
if (!this._mqls) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const matchColumns = this._mqls!.reduce(
|
||||||
|
(cols, mql) => cols + Number(mql.matches),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
// Do -1 column if the menu is docked and open
|
||||||
|
this._columns = Math.max(1, mediaQueryColumns[matchColumns - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 4px 4px env(safe-area-inset-bottom);
|
||||||
|
transform: translateZ(0);
|
||||||
|
position: relative;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
background: var(--lovelace-background, var(--primary-background-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
lit-grid-layout {
|
||||||
|
--placeholder-background-color: var(--accent-color);
|
||||||
|
--resize-handle-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#badges {
|
||||||
|
margin: 8px 16px;
|
||||||
|
font-size: 85%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-fab {
|
||||||
|
position: sticky;
|
||||||
|
float: right;
|
||||||
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
|
bottom: calc(16px + env(safe-area-inset-bottom));
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-fab.rtl {
|
||||||
|
float: left;
|
||||||
|
right: auto;
|
||||||
|
left: calc(16px + env(safe-area-inset-left));
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-grid-view": GridView;
|
||||||
|
}
|
||||||
|
}
|
@@ -114,7 +114,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
if (this.lovelace?.editMode && !editCodeLoaded) {
|
if (this.lovelace?.editMode && !editCodeLoaded) {
|
||||||
editCodeLoaded = true;
|
editCodeLoaded = true;
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "default-layout-editable" */ "./default-view-editable"
|
/* webpackChunkName: "default-view-editable" */ "./default-view-editable"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
yarn.lock
13
yarn.lock
@@ -8608,6 +8608,14 @@ lit-element@2.4.0, lit-element@^2.0.0, lit-element@^2.2.1, lit-element@^2.3.0, l
|
|||||||
dependencies:
|
dependencies:
|
||||||
lit-html "^1.1.1"
|
lit-html "^1.1.1"
|
||||||
|
|
||||||
|
lit-grid-layout@^1.1.11:
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/lit-grid-layout/-/lit-grid-layout-1.1.11.tgz#0eb160ac61fcd25fd1929963776ff5d2df051320"
|
||||||
|
integrity sha512-NSI7QuLllKvjrQEYdAFOv8Ucznu3TWDtxxroyDcv8Qv/zsjkuAJDK/bQjjuDSw/x7yy3NVn/c1I864fSUb47Rw==
|
||||||
|
dependencies:
|
||||||
|
lit-element "^2.4.0"
|
||||||
|
lit-html "^1.3.0"
|
||||||
|
|
||||||
lit-html@1.3.0, lit-html@^1.0.0, lit-html@^1.1.1, lit-html@^1.1.2, lit-html@^1.3.0:
|
lit-html@1.3.0, lit-html@^1.0.0, lit-html@^1.1.1, lit-html@^1.1.2, lit-html@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.3.0.tgz#c80f3cc5793a6dea6c07172be90a70ab20e56034"
|
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.3.0.tgz#c80f3cc5793a6dea6c07172be90a70ab20e56034"
|
||||||
@@ -12583,6 +12591,11 @@ uuid@^3.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
|
uuid@^8.3.0:
|
||||||
|
version "8.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
|
||||||
|
integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
|
||||||
|
|
||||||
v8-compile-cache@^2.0.3:
|
v8-compile-cache@^2.0.3:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
||||||
|
Reference in New Issue
Block a user