mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-15 21:36:36 +00:00
Gallery: Make sidebar collapsible + more tweaks (#11104)
This commit is contained in:
parent
a67799a670
commit
63c113f78d
@ -17,25 +17,33 @@ require("./entry-html.js");
|
|||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
|
||||||
gulp.task("gather-gallery-demos", async function gatherDemos() {
|
gulp.task("gather-gallery-demos", async function gatherDemos() {
|
||||||
const files = await fs.promises.readdir(
|
const demoDir = path.resolve(paths.gallery_dir, "src/demos");
|
||||||
path.resolve(paths.gallery_dir, "src/demos")
|
const files = await fs.promises.readdir(demoDir);
|
||||||
);
|
|
||||||
|
|
||||||
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
||||||
fs.mkdirSync(galleryBuild, { recursive: true });
|
fs.mkdirSync(galleryBuild, { recursive: true });
|
||||||
|
|
||||||
let content = "export const DEMOS = {\n";
|
let content = "export const DEMOS = {\n";
|
||||||
|
|
||||||
|
const processed = new Set();
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.endsWith(".ts")) {
|
let demoId = path.basename(
|
||||||
|
file,
|
||||||
|
file.endsWith(".ts") ? ".ts" : ".markdown"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Can be processed if we saw demo or description before.
|
||||||
|
if (processed.has(demoId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const demoId = path.basename(file, ".ts");
|
|
||||||
const descriptionFile = path.resolve(
|
processed.add(demoId);
|
||||||
paths.gallery_dir,
|
|
||||||
"src/demos",
|
const demoFile = path.resolve(demoDir, `${demoId}.ts`);
|
||||||
`${demoId}.markdown`
|
|
||||||
);
|
const descriptionFile = path.resolve(demoDir, `${demoId}.markdown`);
|
||||||
|
const hasDemo = fs.existsSync(demoFile);
|
||||||
const hasDescription = fs.existsSync(descriptionFile);
|
const hasDescription = fs.existsSync(descriptionFile);
|
||||||
if (hasDescription) {
|
if (hasDescription) {
|
||||||
const descriptionContent = fs.readFileSync(descriptionFile, "utf-8");
|
const descriptionContent = fs.readFileSync(descriptionFile, "utf-8");
|
||||||
@ -55,7 +63,8 @@ gulp.task("gather-gallery-demos", async function gatherDemos() {
|
|||||||
? `description: () => import("${descriptionPath}").then(m => m.default),`
|
? `description: () => import("${descriptionPath}").then(m => m.default),`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
load: () => import("${demoPath}")
|
${hasDemo ? `load: () => import("${demoPath}")` : ""}
|
||||||
|
|
||||||
},\n`;
|
},\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +93,17 @@ gulp.task(
|
|||||||
),
|
),
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
"gen-index-gallery-dev",
|
"gen-index-gallery-dev",
|
||||||
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
|
gulp.parallel(
|
||||||
|
env.useRollup()
|
||||||
|
? "rollup-dev-server-gallery"
|
||||||
|
: "webpack-dev-server-gallery",
|
||||||
|
async function watchMarkdownFiles() {
|
||||||
|
gulp.watch(
|
||||||
|
path.resolve(paths.gallery_dir, "src/demos/*.markdown"),
|
||||||
|
gulp.series("gather-gallery-demos")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
8
gallery/src/demos/demo-introduction.markdown
Normal file
8
gallery/src/demos/demo-introduction.markdown
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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.
|
@ -1,43 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,55 +4,13 @@ import "@material/mwc-top-app-bar-fixed";
|
|||||||
import { html, css, LitElement, PropertyValues } from "lit";
|
import { html, css, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { until } from "lit/directives/until";
|
import { until } from "lit/directives/until";
|
||||||
|
import "../../src/components/ha-card";
|
||||||
import "../../src/components/ha-icon-button";
|
import "../../src/components/ha-icon-button";
|
||||||
import "../../src/managers/notification-manager";
|
import "../../src/managers/notification-manager";
|
||||||
import { haStyle } from "../../src/resources/styles";
|
import { haStyle } from "../../src/resources/styles";
|
||||||
// eslint-disable-next-line import/extensions
|
|
||||||
import { DEMOS } from "../build/import-demos";
|
import { DEMOS } from "../build/import-demos";
|
||||||
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
||||||
|
import { SIDEBAR } from "./sidebar";
|
||||||
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 = {
|
const FAKE_HASS = {
|
||||||
// Just enough for computeRTL for notification-manager
|
// Just enough for computeRTL for notification-manager
|
||||||
@ -65,7 +23,7 @@ const FAKE_HASS = {
|
|||||||
@customElement("ha-gallery")
|
@customElement("ha-gallery")
|
||||||
class HaGallery extends LitElement {
|
class HaGallery extends LitElement {
|
||||||
@property() private _demo =
|
@property() private _demo =
|
||||||
document.location.hash.substring(1) || DEMOS_GROUPED[0].demos![0];
|
document.location.hash.substring(1) || SIDEBAR[0].demos![0];
|
||||||
|
|
||||||
@query("notification-manager")
|
@query("notification-manager")
|
||||||
private _notifications!: HTMLElementTagNameMap["notification-manager"];
|
private _notifications!: HTMLElementTagNameMap["notification-manager"];
|
||||||
@ -73,23 +31,51 @@ class HaGallery extends LitElement {
|
|||||||
@query("mwc-drawer")
|
@query("mwc-drawer")
|
||||||
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
|
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
|
||||||
|
|
||||||
|
private _narrow = window.matchMedia("(max-width: 600px)").matches;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const sidebar: unknown[] = [];
|
||||||
|
|
||||||
|
for (const group of SIDEBAR) {
|
||||||
|
let sectionOpen = false;
|
||||||
|
const links: unknown[] = [];
|
||||||
|
|
||||||
|
for (const demo of group.demos!) {
|
||||||
|
const active = this._demo === demo;
|
||||||
|
if (active) {
|
||||||
|
sectionOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
links.push(html`
|
||||||
|
<a ?active=${active} href=${`#${demo}`}
|
||||||
|
>${group.demoStart === undefined
|
||||||
|
? demo
|
||||||
|
: demo.substring(group.demoStart.length)}</a
|
||||||
|
>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebar.push(
|
||||||
|
group.header
|
||||||
|
? html`
|
||||||
|
<details ?open=${sectionOpen}>
|
||||||
|
<summary class="section">${group.header}</summary>
|
||||||
|
${links}
|
||||||
|
</details>
|
||||||
|
`
|
||||||
|
: links
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<mwc-drawer open hasHeader type="dismissible">
|
<mwc-drawer
|
||||||
|
hasHeader
|
||||||
|
.open=${!this._narrow}
|
||||||
|
.type=${this._narrow ? "modal" : "dismissible"}
|
||||||
|
>
|
||||||
<span slot="title">Home Assistant Design</span>
|
<span slot="title">Home Assistant Design</span>
|
||||||
<!-- <span slot="subtitle">subtitle</span> -->
|
<!-- <span slot="subtitle">subtitle</span> -->
|
||||||
<div class="sidebar">
|
<div class="sidebar">${sidebar}</div>
|
||||||
${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">
|
<div slot="appContent">
|
||||||
<mwc-top-app-bar-fixed>
|
<mwc-top-app-bar-fixed>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@ -106,7 +92,7 @@ class HaGallery extends LitElement {
|
|||||||
${until(
|
${until(
|
||||||
DEMOS[this._demo].description().then(
|
DEMOS[this._demo].description().then(
|
||||||
(content) => html`
|
(content) => html`
|
||||||
<ha-card .header=${this._demo}>
|
<ha-card>
|
||||||
<div class="card-content">${content}</div>
|
<div class="card-content">${content}</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
@ -126,21 +112,12 @@ class HaGallery extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
this.addEventListener("show-notification", (ev) =>
|
this.addEventListener("show-notification", (ev) =>
|
||||||
this._notifications.showDialog({ message: ev.detail.message })
|
this._notifications.showDialog({ message: ev.detail.message })
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addEventListener("alert-dismissed-clicked", () =>
|
this.addEventListener("alert-dismissed-clicked", () =>
|
||||||
this._notifications.showDialog({ message: "Alert dismissed clicked" })
|
this._notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||||
);
|
);
|
||||||
@ -156,12 +133,15 @@ class HaGallery extends LitElement {
|
|||||||
|
|
||||||
window.addEventListener("hashchange", () => {
|
window.addEventListener("hashchange", () => {
|
||||||
this._demo = document.location.hash.substring(1);
|
this._demo = document.location.hash.substring(1);
|
||||||
|
if (this._narrow) {
|
||||||
|
this._drawer.open = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updated(changedProps: PropertyValues) {
|
updated(changedProps: PropertyValues) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("_demo") && this._demo) {
|
if (changedProps.has("_demo") && DEMOS[this._demo].load) {
|
||||||
DEMOS[this._demo].load();
|
DEMOS[this._demo].load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,16 +159,18 @@ class HaGallery extends LitElement {
|
|||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar p {
|
.sidebar details {
|
||||||
margin: 1em 12px;
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar a {
|
.sidebar a {
|
||||||
|
69
gallery/src/sidebar.ts
Normal file
69
gallery/src/sidebar.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { DEMOS } from "../build/import-demos";
|
||||||
|
|
||||||
|
export const SIDEBAR: SidebarSection[] = [
|
||||||
|
{
|
||||||
|
demos: ["introduction"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
// Each section has a header
|
||||||
|
header: "Lovelace",
|
||||||
|
// Specify demos to make sure they are put on top.
|
||||||
|
demos: [],
|
||||||
|
// Add a demoStart to automatically gather demos based on their name
|
||||||
|
demoStart: "hui-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Automation",
|
||||||
|
demoStart: "automation-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Components",
|
||||||
|
demos: [
|
||||||
|
"ha-alert",
|
||||||
|
"ha-bar",
|
||||||
|
"ha-chips",
|
||||||
|
"ha-faded",
|
||||||
|
"ha-form",
|
||||||
|
"ha-label-badge",
|
||||||
|
"ha-selector",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "More Info",
|
||||||
|
demoStart: "more-info-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Rest",
|
||||||
|
demoStart: "", // empty string matches all.
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface SidebarSection {
|
||||||
|
header?: string;
|
||||||
|
demos?: string[];
|
||||||
|
demoStart?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const demosToProcess = new Set(Object.keys(DEMOS));
|
||||||
|
|
||||||
|
for (const group of Object.values(SIDEBAR)) {
|
||||||
|
// Any pre-defined groups will not be sorted.
|
||||||
|
if (group.demos) {
|
||||||
|
for (const demo of group.demos) {
|
||||||
|
demosToProcess.delete(demo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
group.demos = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const group of Object.values(SIDEBAR)) {
|
||||||
|
if (group.demoStart !== undefined) {
|
||||||
|
for (const demo of demosToProcess) {
|
||||||
|
if (demo.startsWith(group.demoStart)) {
|
||||||
|
group.demos!.push(demo);
|
||||||
|
demosToProcess.delete(demo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user