mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Demo
This commit is contained in:
parent
af9b64c6f0
commit
f564420ac8
@ -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"),
|
||||||
sidebar: () => import("../views/hui-sidebar-view"),
|
sidebar: () => import("../views/hui-sidebar-view"),
|
||||||
|
sections: () => import("../views/hui-sections-view"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createViewElement = (
|
export const createViewElement = (
|
||||||
|
@ -10,6 +10,7 @@ import type { HomeAssistant } from "../../../../types";
|
|||||||
import {
|
import {
|
||||||
DEFAULT_VIEW_LAYOUT,
|
DEFAULT_VIEW_LAYOUT,
|
||||||
PANEL_VIEW_LAYOUT,
|
PANEL_VIEW_LAYOUT,
|
||||||
|
SECTIONS_VIEW_LAYOUT,
|
||||||
SIDEBAR_VIEW_LAYOUT,
|
SIDEBAR_VIEW_LAYOUT,
|
||||||
} from "../../views/const";
|
} from "../../views/const";
|
||||||
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||||
@ -53,6 +54,7 @@ export class HuiViewEditor extends LitElement {
|
|||||||
DEFAULT_VIEW_LAYOUT,
|
DEFAULT_VIEW_LAYOUT,
|
||||||
SIDEBAR_VIEW_LAYOUT,
|
SIDEBAR_VIEW_LAYOUT,
|
||||||
PANEL_VIEW_LAYOUT,
|
PANEL_VIEW_LAYOUT,
|
||||||
|
SECTIONS_VIEW_LAYOUT,
|
||||||
] as const
|
] as const
|
||||||
).map((type) => ({
|
).map((type) => ({
|
||||||
value: type,
|
value: type,
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
export const DEFAULT_VIEW_LAYOUT = "masonry";
|
export const DEFAULT_VIEW_LAYOUT = "masonry";
|
||||||
export const PANEL_VIEW_LAYOUT = "panel";
|
export const PANEL_VIEW_LAYOUT = "panel";
|
||||||
export const SIDEBAR_VIEW_LAYOUT = "sidebar";
|
export const SIDEBAR_VIEW_LAYOUT = "sidebar";
|
||||||
export const VIEWS_NO_BADGE_SUPPORT = [PANEL_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT];
|
export const SECTIONS_VIEW_LAYOUT = "sections";
|
||||||
|
export const VIEWS_NO_BADGE_SUPPORT = [
|
||||||
|
PANEL_VIEW_LAYOUT,
|
||||||
|
SIDEBAR_VIEW_LAYOUT,
|
||||||
|
SECTIONS_VIEW_LAYOUT,
|
||||||
|
];
|
||||||
|
211
src/panels/lovelace/views/hui-sections-view.ts
Normal file
211
src/panels/lovelace/views/hui-sections-view.ts
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import { SortableEvent } from "sortablejs";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import "../../../components/entity/ha-state-label-badge";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import type {
|
||||||
|
LovelaceCardConfig,
|
||||||
|
LovelaceViewConfig,
|
||||||
|
LovelaceViewElement,
|
||||||
|
} from "../../../data/lovelace";
|
||||||
|
import { SortableInstance } from "../../../resources/sortable";
|
||||||
|
import { loadSortable } from "../../../resources/sortable.ondemand";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import type { HuiErrorCard } from "../cards/hui-error-card";
|
||||||
|
import type { Lovelace, LovelaceCard } from "../types";
|
||||||
|
|
||||||
|
@customElement("hui-sections-view")
|
||||||
|
export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public lovelace?: Lovelace;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
|
@property({ type: Number }) public index?: number;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public isStrategy = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public cards: Array<
|
||||||
|
LovelaceCard | HuiErrorCard
|
||||||
|
> = [];
|
||||||
|
|
||||||
|
@state() private _config?: LovelaceViewConfig;
|
||||||
|
|
||||||
|
public setConfig(config: LovelaceViewConfig): void {
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cardConfigKeys = new WeakMap<LovelaceCardConfig, string>();
|
||||||
|
|
||||||
|
private _getKey(cardConfig: LovelaceCardConfig) {
|
||||||
|
if (!this._cardConfigKeys.has(cardConfig)) {
|
||||||
|
this._cardConfigKeys.set(cardConfig, Math.random().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._cardConfigKeys.get(cardConfig)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(
|
||||||
|
_changedProperties: Map<string | number | symbol, unknown>
|
||||||
|
): void {
|
||||||
|
this._createSortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
this._destroySortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const cardsConfig = this._config?.cards ?? [];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>Section</h1>
|
||||||
|
${this.lovelace?.editMode
|
||||||
|
? html`
|
||||||
|
<mwc-button outlined @click=${this._addCard}>
|
||||||
|
Add card
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="grid">
|
||||||
|
${repeat(
|
||||||
|
cardsConfig,
|
||||||
|
(cardConfig) => this._getKey(cardConfig),
|
||||||
|
(cardConfig, idx) => {
|
||||||
|
const card = this.cards[idx];
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
draggable: !!this.lovelace?.editMode,
|
||||||
|
})}
|
||||||
|
style=${styleMap({
|
||||||
|
"--row-size": cardConfig.view_layout?.size?.[0],
|
||||||
|
"--column-size": cardConfig.view_layout?.size?.[1],
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
${card}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _sortable?: SortableInstance;
|
||||||
|
|
||||||
|
private async _createSortable() {
|
||||||
|
const Sortable = await loadSortable();
|
||||||
|
this._sortable = new Sortable(this.shadowRoot!.querySelector("#grid")!, {
|
||||||
|
animation: 500,
|
||||||
|
draggable: ".draggable",
|
||||||
|
swapThreshold: 0.5,
|
||||||
|
onSort: (evt: SortableEvent) => {
|
||||||
|
this._dragged(evt);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dragged(ev: SortableEvent): void {
|
||||||
|
if (ev.oldIndex === ev.newIndex) return;
|
||||||
|
this._move(ev.oldIndex!, ev.newIndex!);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _move(index: number, newIndex: number) {
|
||||||
|
const config = this._config;
|
||||||
|
if (config) {
|
||||||
|
const cardsConfig = [...(config.cards ?? [])];
|
||||||
|
|
||||||
|
const cardConfig = cardsConfig.splice(index, 1)[0];
|
||||||
|
cardsConfig.splice(newIndex, 0, cardConfig);
|
||||||
|
const newConfig = {
|
||||||
|
...config,
|
||||||
|
cards: cardsConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.lovelace?.saveConfig({
|
||||||
|
...this.lovelace.config,
|
||||||
|
views: this.lovelace.config.views.map((view, i) =>
|
||||||
|
i === this.index ? newConfig : view
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _destroySortable() {
|
||||||
|
this._sortable?.destroy();
|
||||||
|
this._sortable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addCard(): void {
|
||||||
|
fireEvent(this, "ll-create-card");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
padding-top: 4px;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
--grid-gap: 8px;
|
||||||
|
--grid-cell-height: 64px;
|
||||||
|
--grid-cell-width: 168px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
padding: 0 var(--grid-gap);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
#grid {
|
||||||
|
padding: var(--grid-gap);
|
||||||
|
gap: var(--grid-gap);
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: var(--grid-cell-height);
|
||||||
|
grid-template-columns: repeat(
|
||||||
|
auto-fill,
|
||||||
|
minmax(var(--grid-cell-width), 1fr)
|
||||||
|
);
|
||||||
|
grid-auto-flow: row dense;
|
||||||
|
grid-auto-columns: min-content;
|
||||||
|
}
|
||||||
|
#grid > div {
|
||||||
|
position: relative;
|
||||||
|
grid-row: span var(--row-size, 1);
|
||||||
|
grid-column: span var(--column-size, 1);
|
||||||
|
}
|
||||||
|
.draggable {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
.draggable > * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.sortable-ghost {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-sections-view": SectionsView;
|
||||||
|
}
|
||||||
|
}
|
@ -4831,7 +4831,8 @@
|
|||||||
"types": {
|
"types": {
|
||||||
"masonry": "Masonry (default)",
|
"masonry": "Masonry (default)",
|
||||||
"sidebar": "Sidebar",
|
"sidebar": "Sidebar",
|
||||||
"panel": "Panel (1 card)"
|
"panel": "Panel (1 card)",
|
||||||
|
"sections": "Sections"
|
||||||
},
|
},
|
||||||
"subview": "Subview",
|
"subview": "Subview",
|
||||||
"subview_helper": "Subviews don't appear in tabs and have a back button.",
|
"subview_helper": "Subviews don't appear in tabs and have a back button.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user