Compare commits

...

44 Commits

Author SHA1 Message Date
Zack Arnett
2fef57f0b2 save it 2020-11-12 22:39:34 -06:00
Zack Arnett
9b279632f8 Updates 2020-11-09 11:37:23 -06:00
Zack Barett
480e781364 Restore hui-card-options.ts 2020-11-03 23:04:19 -06:00
Zack Barett
adc10ff0c6 Restore hui-picture-header-footer.ts 2020-11-03 23:03:46 -06:00
Zack Barett
57a718e409 Merge branch 'dev' into lit-grid-layout 2020-11-03 23:01:29 -06:00
Zack Barett
23462bacfa Restore hui-weather-forecast-card.ts 2020-11-03 23:00:02 -06:00
Zack Barett
d99f8b0da8 Restore hui-vertical-stack-card.ts 2020-11-03 22:59:58 -06:00
Zack Barett
991d1f0997 Restore hui-thermostat-card.ts 2020-11-03 22:59:54 -06:00
Zack Barett
54e99357b5 Restore hui-shopping-list-card.ts 2020-11-03 22:59:50 -06:00
Zack Barett
d263b5550a Restore hui-plant-status-card.ts 2020-11-03 22:59:46 -06:00
Zack Barett
a2b6ce3437 Restore hui-media-control-card.ts 2020-11-03 22:59:43 -06:00
Zack Barett
9458f7ae62 Restore hui-markdown-card.ts 2020-11-03 22:59:40 -06:00
Zack Barett
4d2ae36b0d Restore hui-map-card.ts 2020-11-03 22:59:35 -06:00
Zack Barett
ebf0050e81 Restore hui-light-card.ts 2020-11-03 22:59:31 -06:00
Zack Barett
3d0f492d62 Restore hui-iframe-card.ts 2020-11-03 22:59:27 -06:00
Zack Barett
f5d3237b06 Restore hui-humidifier-card.ts 2020-11-03 22:59:23 -06:00
Zack Barett
07671ba1d4 Restore hui-horizontal-stack-card.ts 2020-11-03 22:59:20 -06:00
Zack Barett
354d74ce1d Restore hui-history-graph-card.ts 2020-11-03 22:59:17 -06:00
Zack Barett
e892d14af0 Restore hui-glance-card.ts 2020-11-03 22:59:13 -06:00
Zack Barett
8d034fb7e7 Restore hui-gauge-card.ts 2020-11-03 22:59:10 -06:00
Zack Barett
249456e6a0 Restore hui-entity-filter-card.ts 2020-11-03 22:59:07 -06:00
Zack Barett
d3a6f31310 Restore hui-entity-card.ts 2020-11-03 22:59:03 -06:00
Zack Barett
4c31aead8b Restore hui-entities-card.ts 2020-11-03 22:59:00 -06:00
Zack Barett
3caf91c5d3 Restore hui-button-card.ts 2020-11-03 22:58:56 -06:00
Zack Barett
9c3e754d53 Restore hui-alarm-panel-card.ts 2020-11-03 22:58:53 -06:00
Zack Barett
f48765c2b6 Restore ha-card.ts 2020-11-03 22:58:43 -06:00
Zack Arnett
236f26e652 SOME CHANGES 2020-11-03 22:57:54 -06:00
Zack Arnett
0fed96aba3 idk what I just did. I never commit just work 2020-10-09 16:33:22 -05:00
Zack Arnett
70b77833f1 add grid card options 2020-10-04 16:49:10 -05:00
Zack Arnett
307694a820 updates 2020-10-01 11:17:35 -05:00
Zack Barett
402391c3e1 Restore hui-masonry-view.ts 2020-09-30 20:57:06 -05:00
Zack Barett
81b5866e4d Restore dialog-entity-editor.ts 2020-09-30 20:55:52 -05:00
Zack Barett
09985bf5f7 Restore ha-device-actions-ozw.ts 2020-09-30 20:55:47 -05:00
Zack Barett
71d12a9e7a Restore ha-automation-action-wait_template.ts 2020-09-30 20:55:41 -05:00
Zack Barett
5fd733b6c9 Restore ha-more-info-logbook.ts 2020-09-30 20:55:34 -05:00
Zack Arnett
bc3f827b4a Merge branch 'dev' of https://github.com/home-assistant/frontend into lit-grid-layout 2020-09-30 20:54:25 -05:00
Zack Arnett
a807a182e4 fix card options 2020-09-30 20:53:40 -05:00
Zack Barett
0a1fb843ca Restore hui-view.ts 2020-09-30 20:50:04 -05:00
Zack Arnett
fceb1568f5 add Grid view 2020-09-30 20:49:38 -05:00
Zack Barett
74e93bbefc Restore ha-panel-lovelace.ts 2020-09-30 20:31:42 -05:00
Zack Barett
5c0f4b564b Restore lovelace.ts 2020-09-30 20:31:11 -05:00
Zack Barett
76734f7a0b Restore types.ts 2020-09-30 20:29:07 -05:00
Zack Arnett
440d10e4cd Merge branch 'dev' into lit-grid-layout 2020-08-04 09:17:23 -05:00
Zack Arnett
a901072695 First changes, alot of work to do 2020-08-04 08:53:07 -05:00
10 changed files with 584 additions and 1 deletions

View File

@@ -35,6 +35,9 @@ const createWebpackConfig = ({
loader: "babel-loader",
options: bundle.babelOptions({ latestBuild }),
},
resolve: {
fullySpecified: false,
},
},
{
test: /\.css$/,

View File

@@ -105,6 +105,7 @@
"leaflet": "^1.4.0",
"leaflet-draw": "^1.0.4",
"lit-element": "^2.4.0",
"lit-grid-layout": "^1.1.11",
"lit-html": "^1.3.0",
"lit-virtualizer": "^0.4.2",
"marked": "^1.1.1",
@@ -121,6 +122,7 @@
"superstruct": "^0.10.12",
"tinykeys": "^1.1.1",
"unfetch": "^4.1.0",
"uuid": "^8.3.0",
"vue": "^2.6.11",
"vue2-daterange-picker": "^0.5.1",
"web-animations-js": "^2.3.2",

View File

@@ -9,6 +9,7 @@ const ALWAYS_LOADED_LAYOUTS = new Set(["masonry"]);
const LAZY_LOAD_LAYOUTS = {
panel: () => import("../views/hui-panel-view"),
grid: () => import("../views/hui-grid-view"),
};
export const createViewElement = (

View File

@@ -13,6 +13,7 @@ import {
TemplateResult,
} from "lit-element";
import {
any,
array,
assert,
boolean,
@@ -62,6 +63,7 @@ const cardConfigStruct = object({
theme: optional(string()),
show_header_toggle: optional(boolean()),
state_color: optional(boolean()),
layout: optional(any()),
entities: array(entitiesConfigStruct),
header: optional(headerFooterConfigStructs),
footer: optional(headerFooterConfigStructs),

View File

@@ -36,6 +36,7 @@ export interface LovelaceCard extends HTMLElement {
hass?: HomeAssistant;
isPanel?: boolean;
editMode?: boolean;
layout?: any;
getCardSize(): number | Promise<number>;
setConfig(config: LovelaceCardConfig): void;
}

View File

@@ -0,0 +1,3 @@
// hui-grid-view dependencies for when in edit mode.
import "@material/mwc-fab";
import "./hui-grid-card-options";

View 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;
}
}

View 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;
}
}

View File

@@ -114,7 +114,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
if (this.lovelace?.editMode && !editCodeLoaded) {
editCodeLoaded = true;
import(
/* webpackChunkName: "default-layout-editable" */ "./default-view-editable"
/* webpackChunkName: "default-view-editable" */ "./default-view-editable"
);
}

View File

@@ -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:
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:
version "1.3.0"
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"
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:
version "2.1.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"