Gallery: Make sidebar collapsible + more tweaks (#11104)

This commit is contained in:
Paulus Schoutsen 2022-01-06 01:21:17 -08:00 committed by GitHub
parent a67799a670
commit 63c113f78d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 130 deletions

View File

@ -17,25 +17,33 @@ require("./entry-html.js");
require("./rollup.js");
gulp.task("gather-gallery-demos", async function gatherDemos() {
const files = await fs.promises.readdir(
path.resolve(paths.gallery_dir, "src/demos")
);
const demoDir = path.resolve(paths.gallery_dir, "src/demos");
const files = await fs.promises.readdir(demoDir);
const galleryBuild = path.resolve(paths.gallery_dir, "build");
fs.mkdirSync(galleryBuild, { recursive: true });
let content = "export const DEMOS = {\n";
const processed = new Set();
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;
}
const demoId = path.basename(file, ".ts");
const descriptionFile = path.resolve(
paths.gallery_dir,
"src/demos",
`${demoId}.markdown`
);
processed.add(demoId);
const demoFile = path.resolve(demoDir, `${demoId}.ts`);
const descriptionFile = path.resolve(demoDir, `${demoId}.markdown`);
const hasDemo = fs.existsSync(demoFile);
const hasDescription = fs.existsSync(descriptionFile);
if (hasDescription) {
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),`
: ""
}
load: () => import("${demoPath}")
${hasDemo ? `load: () => import("${demoPath}")` : ""}
},\n`;
}
@ -84,7 +93,17 @@ gulp.task(
),
"copy-static-gallery",
"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")
);
}
)
)
);

View 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.

View File

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

View File

@ -4,55 +4,13 @@ import "@material/mwc-top-app-bar-fixed";
import { html, css, LitElement, PropertyValues } from "lit";
import { customElement, property, query } from "lit/decorators";
import { until } from "lit/directives/until";
import "../../src/components/ha-card";
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);
}
}
}
}
import { SIDEBAR } from "./sidebar";
const FAKE_HASS = {
// Just enough for computeRTL for notification-manager
@ -65,7 +23,7 @@ const FAKE_HASS = {
@customElement("ha-gallery")
class HaGallery extends LitElement {
@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")
private _notifications!: HTMLElementTagNameMap["notification-manager"];
@ -73,23 +31,51 @@ class HaGallery extends LitElement {
@query("mwc-drawer")
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
private _narrow = window.matchMedia("(max-width: 600px)").matches;
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`
<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="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 class="sidebar">${sidebar}</div>
<div slot="appContent">
<mwc-top-app-bar-fixed>
<ha-icon-button
@ -106,7 +92,7 @@ class HaGallery extends LitElement {
${until(
DEMOS[this._demo].description().then(
(content) => html`
<ha-card .header=${this._demo}>
<ha-card>
<div class="card-content">${content}</div>
</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) {
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" })
);
@ -156,12 +133,15 @@ class HaGallery extends LitElement {
window.addEventListener("hashchange", () => {
this._demo = document.location.hash.substring(1);
if (this._narrow) {
this._drawer.open = false;
}
});
}
updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("_demo") && this._demo) {
if (changedProps.has("_demo") && DEMOS[this._demo].load) {
DEMOS[this._demo].load();
}
}
@ -179,16 +159,18 @@ class HaGallery extends LitElement {
-moz-user-select: initial;
}
.section {
font-weight: bold;
}
.sidebar {
padding: 4px;
}
.sidebar p {
margin: 1em 12px;
.sidebar details {
margin-top: 1em;
}
.sidebar summary {
cursor: pointer;
font-weight: bold;
margin-bottom: 8px;
}
.sidebar a {

69
gallery/src/sidebar.ts Normal file
View 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);
}
}
}
}