mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
Convert ha-gallery to TS (#11102)
* Convert ha-gallery to TS * Prepare gallery to be design portal * Import card in introduction * Clean up demo IDs * Convert demo-cards * TypeScript conversion of demo-card * Fix default demo
This commit is contained in:
parent
76a4b1efbd
commit
e3d78d6dc5
@ -25,7 +25,7 @@ gulp.task("gather-gallery-demos", async function gatherDemos() {
|
||||
for (const file of files) {
|
||||
const demoId = path.basename(file, ".ts");
|
||||
const demoPath = "../src/demos/" + demoId;
|
||||
content += ` "${demoId}": () => import("${demoPath}"),\n`;
|
||||
content += ` "${demoId.substring(5)}": () => import("${demoPath}"),\n`;
|
||||
}
|
||||
|
||||
content += "};";
|
||||
|
@ -1,129 +0,0 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { load } from "js-yaml";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
|
||||
class DemoCard extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
.root {
|
||||
display: flex;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h2 small {
|
||||
font-size: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
}
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h2>
|
||||
[[config.heading]]
|
||||
<template is="dom-if" if="[[_size]]">
|
||||
<small>(size [[_size]])</small>
|
||||
</template>
|
||||
</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
<pre>[[_trim(config.config)]]</pre>
|
||||
</template>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
observer: "_hassChanged",
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
observer: "_configChanged",
|
||||
},
|
||||
showConfig: Boolean,
|
||||
_size: {
|
||||
type: Number,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
}
|
||||
|
||||
_configChanged(config) {
|
||||
const card = this.$.card;
|
||||
while (card.lastChild) {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement(load(config.config)[0]);
|
||||
card.appendChild(el);
|
||||
this._getSize(el);
|
||||
}
|
||||
|
||||
async _getSize(el) {
|
||||
await customElements.whenDefined(el.localName);
|
||||
|
||||
if (!("getCardSize" in el)) {
|
||||
this._size = undefined;
|
||||
return;
|
||||
}
|
||||
this._size = await el.getCardSize();
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
const element = createCardElement(cardConfig);
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
}
|
||||
element.addEventListener(
|
||||
"ll-rebuild",
|
||||
(ev) => {
|
||||
ev.stopPropagation();
|
||||
this._rebuildCard(element, cardConfig);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
return element;
|
||||
}
|
||||
|
||||
_rebuildCard(cardElToReplace, config) {
|
||||
const newCardEl = this._createCardElement(config);
|
||||
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
|
||||
}
|
||||
|
||||
_hassChanged(hass) {
|
||||
const card = this.$.card.lastChild;
|
||||
if (card) card.hass = hass;
|
||||
}
|
||||
|
||||
_trim(config) {
|
||||
return config.trim();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-card", DemoCard);
|
129
gallery/src/components/demo-card.ts
Normal file
129
gallery/src/components/demo-card.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { load } from "js-yaml";
|
||||
import { html, css, LitElement, PropertyValues } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
export interface DemoCardConfig {
|
||||
heading: string;
|
||||
config: string;
|
||||
}
|
||||
|
||||
@customElement("demo-card")
|
||||
class DemoCard extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public config!: DemoCardConfig;
|
||||
|
||||
@property() public showConfig = false;
|
||||
|
||||
@state() private _size?: number;
|
||||
|
||||
@query("#card") private _card!: HTMLElement;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h2>
|
||||
${this.config.heading}
|
||||
${this._size !== undefined
|
||||
? html`<small>(size ${this._size})</small>`
|
||||
: ""}
|
||||
</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("config")) {
|
||||
const card = this._card;
|
||||
while (card.lastChild) {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement((load(this.config.config) as any)[0]);
|
||||
card.appendChild(el);
|
||||
this._getSize(el);
|
||||
}
|
||||
|
||||
if (changedProps.has("hass")) {
|
||||
const card = this._card.lastChild;
|
||||
if (card) {
|
||||
(card as any).hass = this.hass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _getSize(el) {
|
||||
await customElements.whenDefined(el.localName);
|
||||
|
||||
if (!("getCardSize" in el)) {
|
||||
this._size = undefined;
|
||||
return;
|
||||
}
|
||||
this._size = await el.getCardSize();
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
const element = createCardElement(cardConfig);
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
}
|
||||
element.addEventListener(
|
||||
"ll-rebuild",
|
||||
(ev) => {
|
||||
ev.stopPropagation();
|
||||
this._rebuildCard(element, cardConfig);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
return element;
|
||||
}
|
||||
|
||||
_rebuildCard(cardElToReplace, config) {
|
||||
const newCardEl = this._createCardElement(config);
|
||||
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.root {
|
||||
display: flex;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h2 small {
|
||||
font-size: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
}
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-card": DemoCard;
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "./demo-card";
|
||||
|
||||
class DemoCards extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
#container {
|
||||
min-height: calc(100vh - 128px);
|
||||
background: var(--primary-background-color);
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
demo-card {
|
||||
margin: 16px 16px 32px;
|
||||
}
|
||||
app-toolbar {
|
||||
background-color: var(--light-primary-color);
|
||||
}
|
||||
.filters {
|
||||
margin-left: 60px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<app-toolbar>
|
||||
<div class="filters">
|
||||
<ha-formfield label="Show config">
|
||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Dark theme">
|
||||
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
<div id="container">
|
||||
<div class="cards">
|
||||
<template is="dom-repeat" items="[[configs]]">
|
||||
<demo-card
|
||||
config="[[item]]"
|
||||
show-config="[[_showConfig]]"
|
||||
hass="[[hass]]"
|
||||
></demo-card>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
configs: Object,
|
||||
hass: Object,
|
||||
_showConfig: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_showConfigToggled(ev) {
|
||||
this._showConfig = ev.target.checked;
|
||||
}
|
||||
|
||||
_darkThemeToggled(ev) {
|
||||
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
|
||||
dark: ev.target.checked,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-cards", DemoCards);
|
92
gallery/src/components/demo-cards.ts
Normal file
92
gallery/src/components/demo-cards.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "./demo-card";
|
||||
import type { DemoCardConfig } from "./demo-card";
|
||||
|
||||
@customElement("demo-cards")
|
||||
class DemoCards extends LitElement {
|
||||
@property() public configs!: DemoCardConfig[];
|
||||
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@state() private _showConfig = false;
|
||||
|
||||
@query("#container") private _container!: HTMLElement;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<app-toolbar>
|
||||
<div class="filters">
|
||||
<ha-formfield label="Show config">
|
||||
<ha-switch
|
||||
.checked=${this._showConfig}
|
||||
@change=${this._showConfigToggled}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Dark theme">
|
||||
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
<div id="container">
|
||||
<div class="cards">
|
||||
${this.configs.map(
|
||||
(config) => html`
|
||||
<demo-card
|
||||
.config=${config}
|
||||
.showConfig=${this._showConfig}
|
||||
.hass=${this.hass}
|
||||
></demo-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
_showConfigToggled(ev) {
|
||||
this._showConfig = ev.target.checked;
|
||||
}
|
||||
|
||||
_darkThemeToggled(ev) {
|
||||
applyThemesOnElement(this._container, { themes: {} } as any, "default", {
|
||||
dark: ev.target.checked,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
#container {
|
||||
min-height: calc(100vh - 128px);
|
||||
background: var(--primary-background-color);
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
demo-card {
|
||||
margin: 16px 16px 32px;
|
||||
}
|
||||
app-toolbar {
|
||||
background-color: var(--light-primary-color);
|
||||
}
|
||||
.filters {
|
||||
margin-left: 60px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-cards": DemoCards;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const CONFIGS = [
|
||||
@ -38,9 +39,16 @@ const CONFIGS = [
|
||||
|
||||
@customElement("demo-hui-iframe-card")
|
||||
class DemoIframe extends LitElement {
|
||||
@query("demo-cards") private _demos!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
provideHass(this._demos);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -73,6 +73,6 @@ class DemoHuiMediaPlayerRow extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
|
||||
"demo-hui-media-player-rows": DemoHuiMediaPlayerRow;
|
||||
}
|
||||
}
|
43
gallery/src/demos/demo-introduction.ts
Normal file
43
gallery/src/demos/demo-introduction.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-markdown";
|
||||
|
||||
@customElement("demo-introduction")
|
||||
export class DemoIntroduction extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Welcome!">
|
||||
<div class="card-content">
|
||||
<ha-markdown
|
||||
.content=${`
|
||||
Lovelace has many different cards. Each card allows the user to tell
|
||||
a different story about what is going on in their house. These cards
|
||||
are very customizable, as no household is the same.
|
||||
|
||||
This gallery helps our developers and designers to see all the
|
||||
different states that each card can be in.
|
||||
|
||||
Check [the Lovelace documentation](https://www.home-assistant.io/lovelace) for instructions on how to get started with Lovelace.
|
||||
`}
|
||||
></ha-markdown>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-introduction": DemoIntroduction;
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../src/components/ha-card";
|
||||
import "../../src/components/ha-icon";
|
||||
import "../../src/components/ha-icon-button";
|
||||
import "../../src/managers/notification-manager";
|
||||
import "../../src/styles/polymer-ha-style";
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { DEMOS } from "../build/import-demos";
|
||||
|
||||
class HaGallery extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-positioning ha-style">
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
app-header-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
ha-icon-button.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.pickers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.pickers ha-card {
|
||||
width: 400px;
|
||||
display: block;
|
||||
margin: 16px 8px;
|
||||
}
|
||||
|
||||
.pickers ha-card:last-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin: -1em 0;
|
||||
}
|
||||
|
||||
p a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-icon-button
|
||||
on-click="_backTapped"
|
||||
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||
>
|
||||
<ha-icon icon="hass:arrow-left"></ha-icon>
|
||||
</ha-icon-button>
|
||||
<div main-title>
|
||||
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||
</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class="content">
|
||||
<div id="demo"></div>
|
||||
<template is="dom-if" if="[[!_demo]]">
|
||||
<div class="pickers">
|
||||
<ha-card header="Lovelace Card Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
Lovelace has many different cards. Each card allows the user
|
||||
to tell a different story about what is going on in their
|
||||
house. These cards are very customizable, as no household is
|
||||
the same.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This gallery helps our developers and designers to see all
|
||||
the different states that each card can be in.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Check
|
||||
<a href="https://www.home-assistant.io/lovelace"
|
||||
>the official website</a
|
||||
>
|
||||
for instructions on how to get started with Lovelace.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_lovelaceDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Other Demos">
|
||||
<div class="card-content intro"></div>
|
||||
<template is="dom-repeat" items="[[_restDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
<notification-manager
|
||||
hass="[[_fakeHass]]"
|
||||
id="notifications"
|
||||
></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_fakeHass: {
|
||||
type: Object,
|
||||
// Just enough for computeRTL
|
||||
value: {
|
||||
language: "en",
|
||||
translationMetadata: {
|
||||
translations: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
_demo: {
|
||||
type: String,
|
||||
value: document.location.hash.substr(1),
|
||||
observer: "_demoChanged",
|
||||
},
|
||||
_demos: {
|
||||
type: Array,
|
||||
value: Object.keys(DEMOS),
|
||||
},
|
||||
_lovelaceDemos: {
|
||||
type: Array,
|
||||
computed: "_computeLovelace(_demos)",
|
||||
},
|
||||
_restDemos: {
|
||||
type: Array,
|
||||
computed: "_computeRest(_demos)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
|
||||
this.addEventListener("show-notification", (ev) =>
|
||||
this.$.notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-dismissed-clicked", () =>
|
||||
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||
);
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this.$.notifications.showDialog({
|
||||
message: `Showing more info for ${ev.detail.entityId}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("hashchange", () => {
|
||||
this._demo = document.location.hash.substr(1);
|
||||
});
|
||||
}
|
||||
|
||||
_withDefault(value, def) {
|
||||
return value || def;
|
||||
}
|
||||
|
||||
_demoChanged(demo) {
|
||||
const root = this.$.demo;
|
||||
|
||||
while (root.lastChild) root.removeChild(root.lastChild);
|
||||
|
||||
if (demo) {
|
||||
DEMOS[demo]();
|
||||
const el = document.createElement(demo);
|
||||
root.appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
_computeHeaderButtonClass(demo) {
|
||||
return demo ? "" : "invisible";
|
||||
}
|
||||
|
||||
_backTapped() {
|
||||
document.location.hash = "";
|
||||
}
|
||||
|
||||
_computeLovelace(demos) {
|
||||
return demos.filter((demo) => demo.includes("hui"));
|
||||
}
|
||||
|
||||
_computeRest(demos) {
|
||||
return demos.filter((demo) => !demo.includes("hui"));
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-gallery", HaGallery);
|
207
gallery/src/ha-gallery.ts
Normal file
207
gallery/src/ha-gallery.ts
Normal file
@ -0,0 +1,207 @@
|
||||
import { mdiMenu } from "@mdi/js";
|
||||
import "@material/mwc-drawer";
|
||||
import "@material/mwc-top-app-bar-fixed";
|
||||
import { html, css, LitElement, PropertyValues } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../src/components/ha-icon-button";
|
||||
import "../../src/managers/notification-manager";
|
||||
import { haStyle } from "../../src/resources/styles";
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { DEMOS } from "../build/import-demos";
|
||||
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
||||
|
||||
const DEMOS_GROUPED: {
|
||||
header?: string;
|
||||
demos?: string[];
|
||||
demoStart?: string;
|
||||
}[] = [
|
||||
{
|
||||
demos: ["introduction"],
|
||||
},
|
||||
{
|
||||
header: "Lovelace",
|
||||
demoStart: "hui-",
|
||||
},
|
||||
{
|
||||
header: "Automation",
|
||||
demoStart: "automation-",
|
||||
},
|
||||
{
|
||||
header: "Rest",
|
||||
demoStart: "",
|
||||
},
|
||||
];
|
||||
|
||||
const demosToProcess = new Set(Object.keys(DEMOS));
|
||||
|
||||
for (const group of Object.values(DEMOS_GROUPED)) {
|
||||
if (group.demos) {
|
||||
for (const demo of group.demos) {
|
||||
demosToProcess.delete(demo);
|
||||
}
|
||||
}
|
||||
if (!group.demos) {
|
||||
group.demos = [];
|
||||
}
|
||||
if (group.demoStart !== undefined) {
|
||||
for (const demo of demosToProcess) {
|
||||
if (demo.startsWith(group.demoStart)) {
|
||||
group.demos.push(demo);
|
||||
demosToProcess.delete(demo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FAKE_HASS = {
|
||||
// Just enough for computeRTL for notification-manager
|
||||
language: "en",
|
||||
translationMetadata: {
|
||||
translations: {},
|
||||
},
|
||||
};
|
||||
|
||||
@customElement("ha-gallery")
|
||||
class HaGallery extends LitElement {
|
||||
@property() private _demo =
|
||||
document.location.hash.substring(1) || DEMOS_GROUPED[0].demos![0];
|
||||
|
||||
@query("notification-manager")
|
||||
private _notifications!: HTMLElementTagNameMap["notification-manager"];
|
||||
|
||||
@query("mwc-drawer")
|
||||
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mwc-drawer open hasHeader type="dismissible">
|
||||
<span slot="title">Home Assistant Design</span>
|
||||
<!-- <span slot="subtitle">subtitle</span> -->
|
||||
<div class="sidebar">
|
||||
${DEMOS_GROUPED.map(
|
||||
(group) => html`
|
||||
${group.header
|
||||
? html`<p class="section">${group.header}</p>`
|
||||
: ""}
|
||||
${group.demos!.map((demo) =>
|
||||
this._renderDemo(demo, group.demoStart)
|
||||
)}
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div slot="appContent">
|
||||
<mwc-top-app-bar-fixed>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this._menuTapped}
|
||||
.path=${mdiMenu}
|
||||
></ha-icon-button>
|
||||
|
||||
<div slot="title">${this._demo}</div>
|
||||
</mwc-top-app-bar-fixed>
|
||||
<div>${dynamicElement(`demo-${this._demo}`)}</div>
|
||||
</div>
|
||||
</mwc-drawer>
|
||||
<notification-manager
|
||||
.hass=${FAKE_HASS}
|
||||
id="notifications"
|
||||
></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderDemo(demo: string, demoStart?: string) {
|
||||
return html`
|
||||
<a ?active=${this._demo === demo} href=${`#${demo}`}
|
||||
>${demoStart === undefined ? demo : demo.substring(demoStart.length)}</a
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
this.addEventListener("show-notification", (ev) =>
|
||||
this._notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-dismissed-clicked", () =>
|
||||
this._notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||
);
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this._notifications.showDialog({
|
||||
message: `Showing more info for ${ev.detail.entityId}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.location.hash = this._demo;
|
||||
|
||||
window.addEventListener("hashchange", () => {
|
||||
this._demo = document.location.hash.substring(1);
|
||||
});
|
||||
}
|
||||
|
||||
updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("_demo") && this._demo) {
|
||||
DEMOS[this._demo]();
|
||||
}
|
||||
}
|
||||
|
||||
_menuTapped() {
|
||||
this._drawer.open = !this._drawer.open;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.section {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.sidebar p {
|
||||
margin: 1em 12px;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: var(--primary-text-color);
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar a[active]::before {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
bottom: 0;
|
||||
left: 2px;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
transition: opacity 15ms linear;
|
||||
will-change: opacity;
|
||||
background-color: var(--sidebar-selected-icon-color);
|
||||
opacity: 0.12;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-gallery": HaGallery;
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@
|
||||
"@material/mwc-checkbox": "0.25.3",
|
||||
"@material/mwc-circular-progress": "0.25.3",
|
||||
"@material/mwc-dialog": "0.25.3",
|
||||
"@material/mwc-drawer": "^0.25.3",
|
||||
"@material/mwc-fab": "0.25.3",
|
||||
"@material/mwc-formfield": "0.25.3",
|
||||
"@material/mwc-icon-button": "patch:@material/mwc-icon-button@0.25.3#./.yarn/patches/@material/mwc-icon-button/remove-icon.patch",
|
||||
@ -66,6 +67,7 @@
|
||||
"@material/mwc-tab": "0.25.3",
|
||||
"@material/mwc-tab-bar": "0.25.3",
|
||||
"@material/mwc-textfield": "0.25.3",
|
||||
"@material/mwc-top-app-bar-fixed": "^0.25.3",
|
||||
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
|
||||
"@mdi/js": "6.5.95",
|
||||
"@mdi/svg": "6.5.95",
|
||||
|
@ -92,6 +92,10 @@ class NotificationManager extends LitElement {
|
||||
customElements.define("notification-manager", NotificationManager);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"notification-manager": NotificationManager;
|
||||
}
|
||||
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"hass-notification": ShowToastParams;
|
||||
|
62
yarn.lock
62
yarn.lock
@ -2126,6 +2126,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/drawer@npm:=14.0.0-canary.261f2db59.0":
|
||||
version: 14.0.0-canary.261f2db59.0
|
||||
resolution: "@material/drawer@npm:14.0.0-canary.261f2db59.0"
|
||||
dependencies:
|
||||
"@material/animation": 14.0.0-canary.261f2db59.0
|
||||
"@material/base": 14.0.0-canary.261f2db59.0
|
||||
"@material/dom": 14.0.0-canary.261f2db59.0
|
||||
"@material/elevation": 14.0.0-canary.261f2db59.0
|
||||
"@material/feature-targeting": 14.0.0-canary.261f2db59.0
|
||||
"@material/list": 14.0.0-canary.261f2db59.0
|
||||
"@material/ripple": 14.0.0-canary.261f2db59.0
|
||||
"@material/rtl": 14.0.0-canary.261f2db59.0
|
||||
"@material/shape": 14.0.0-canary.261f2db59.0
|
||||
"@material/theme": 14.0.0-canary.261f2db59.0
|
||||
"@material/typography": 14.0.0-canary.261f2db59.0
|
||||
tslib: ^2.1.0
|
||||
checksum: 92146c61e00220f7aab80e3631c752e11147bee22e4a09e7bac21d233ae0aeff9545efe4d4a0fb10e889161ba06433ce9222bdcfda73de3ea54027cdadb3571a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/elevation@npm:14.0.0-canary.261f2db59.0":
|
||||
version: 14.0.0-canary.261f2db59.0
|
||||
resolution: "@material/elevation@npm:14.0.0-canary.261f2db59.0"
|
||||
@ -2343,6 +2363,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/mwc-drawer@npm:^0.25.3":
|
||||
version: 0.25.3
|
||||
resolution: "@material/mwc-drawer@npm:0.25.3"
|
||||
dependencies:
|
||||
"@material/drawer": =14.0.0-canary.261f2db59.0
|
||||
"@material/mwc-base": ^0.25.3
|
||||
blocking-elements: ^0.1.0
|
||||
lit: ^2.0.0
|
||||
tslib: ^2.0.1
|
||||
wicg-inert: ^3.0.0
|
||||
checksum: 1195301cd943c1f7dae92ef33c383f5a2c3238d26fa44dd006d3c90d1c101cf17a5e67e491ef6ec49889a5cef682ea2f410397ac76be556cde1c8cb79f06874d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/mwc-fab@npm:0.25.3":
|
||||
version: 0.25.3
|
||||
resolution: "@material/mwc-fab@npm:0.25.3"
|
||||
@ -2624,6 +2658,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/mwc-top-app-bar-fixed@npm:^0.25.3":
|
||||
version: 0.25.3
|
||||
resolution: "@material/mwc-top-app-bar-fixed@npm:0.25.3"
|
||||
dependencies:
|
||||
"@material/mwc-top-app-bar": ^0.25.3
|
||||
"@material/top-app-bar": =14.0.0-canary.261f2db59.0
|
||||
lit: ^2.0.0
|
||||
tslib: ^2.0.1
|
||||
checksum: f142b0625b0b5bd9335b43db977a20502da49707415c69bb8df8ba3626f81cc4c8283aeeece6f20d3a401f370b33ac88a41ebdcaa04fe90d5b2067142b1c460c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/mwc-top-app-bar@npm:^0.25.3":
|
||||
version: 0.25.3
|
||||
resolution: "@material/mwc-top-app-bar@npm:0.25.3"
|
||||
dependencies:
|
||||
"@material/mwc-base": ^0.25.3
|
||||
"@material/top-app-bar": =14.0.0-canary.261f2db59.0
|
||||
lit: ^2.0.0
|
||||
tslib: ^2.0.1
|
||||
checksum: c71ad8f557232827bab4b0cdf69fc3aa8caa9aa84193a9b085d3504c7d5c7a8d1e17aab7ebcf84cd0983e2ada8c88cc28de3f1d3a824c0b4fb9ce2ba96b48912
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/notched-outline@npm:14.0.0-canary.261f2db59.0, @material/notched-outline@npm:=14.0.0-canary.261f2db59.0":
|
||||
version: 14.0.0-canary.261f2db59.0
|
||||
resolution: "@material/notched-outline@npm:14.0.0-canary.261f2db59.0"
|
||||
@ -2869,7 +2927,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material/top-app-bar@npm:14.0.0-canary.261f2db59.0":
|
||||
"@material/top-app-bar@npm:14.0.0-canary.261f2db59.0, @material/top-app-bar@npm:=14.0.0-canary.261f2db59.0":
|
||||
version: 14.0.0-canary.261f2db59.0
|
||||
resolution: "@material/top-app-bar@npm:14.0.0-canary.261f2db59.0"
|
||||
dependencies:
|
||||
@ -9022,6 +9080,7 @@ fsevents@^1.2.7:
|
||||
"@material/mwc-checkbox": 0.25.3
|
||||
"@material/mwc-circular-progress": 0.25.3
|
||||
"@material/mwc-dialog": 0.25.3
|
||||
"@material/mwc-drawer": ^0.25.3
|
||||
"@material/mwc-fab": 0.25.3
|
||||
"@material/mwc-formfield": 0.25.3
|
||||
"@material/mwc-icon-button": "patch:@material/mwc-icon-button@0.25.3#./.yarn/patches/@material/mwc-icon-button/remove-icon.patch"
|
||||
@ -9036,6 +9095,7 @@ fsevents@^1.2.7:
|
||||
"@material/mwc-tab": 0.25.3
|
||||
"@material/mwc-tab-bar": 0.25.3
|
||||
"@material/mwc-textfield": 0.25.3
|
||||
"@material/mwc-top-app-bar-fixed": ^0.25.3
|
||||
"@material/top-app-bar": 14.0.0-canary.261f2db59.0
|
||||
"@mdi/js": 6.5.95
|
||||
"@mdi/svg": 6.5.95
|
||||
|
Loading…
x
Reference in New Issue
Block a user