Use shopping list card in panel (#7519)

This commit is contained in:
Ian Richardson 2020-11-05 09:17:42 -06:00 committed by GitHub
parent 1c9d0200ca
commit 9a3a7c28f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 133 additions and 251 deletions

View File

@ -13,7 +13,6 @@
"src/panels/iframe/ha-panel-iframe.js",
"src/panels/logbook/ha-panel-logbook.js",
"src/panels/map/ha-panel-map.js",
"src/panels/shopping-list/ha-panel-shopping-list.js",
"src/panels/mailbox/ha-panel-mailbox.js",
"hassio/src/entrypoint.js"
],

View File

@ -1,245 +0,0 @@
import "../../layouts/ha-app-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-checkbox/paper-checkbox";
import "../../components/ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-listbox/paper-listbox";
import "@material/mwc-list/mwc-list-item";
import "../../components/ha-button-menu";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import "../../components/ha-card";
import "../../components/ha-menu-button";
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import LocalizeMixin from "../../mixins/localize-mixin";
import "../../styles/polymer-ha-style";
import { mdiDotsVertical } from "@mdi/js";
/*
* @appliesMixin LocalizeMixin
*/
class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style include="ha-style">
:host {
height: 100%;
}
app-toolbar paper-listbox {
width: 150px;
}
app-toolbar paper-item {
cursor: pointer;
}
.content {
padding-bottom: 32px;
max-width: 600px;
margin: 0 auto;
}
paper-icon-item {
border-top: 1px solid var(--divider-color);
}
paper-icon-item:first-child {
border-top: 0;
}
paper-checkbox {
padding: 11px;
}
paper-input {
--paper-input-container-underline: {
display: none;
}
--paper-input-container-underline-focus: {
display: none;
}
position: relative;
top: 1px;
}
.tip {
padding: 24px;
text-align: center;
color: var(--secondary-text-color);
}
</style>
<ha-app-layout>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button
hass="[[hass]]"
narrow="[[narrow]]"
></ha-menu-button>
<div main-title>[[localize('panel.shopping_list')]]</div>
<ha-icon-button
hidden$="[[!conversation]]"
aria-label="Start conversation"
icon="hass:microphone"
on-click="_showVoiceCommandDialog"
></ha-icon-button>
<ha-button-menu corner="BOTTOM_START" on-action="_clearCompleted">
<ha-icon-button
icon="hass:dots-vertical"
label="Menu"
slot="trigger"
>
</ha-icon-button>
<mwc-list-item>
[[localize('ui.panel.shopping-list.clear_completed')]]
</mwc-list-item>
</ha-button-menu>
</app-toolbar>
</app-header>
<div class="content">
<ha-card>
<paper-icon-item on-focus="_focusRowInput">
<ha-icon-button
slot="item-icon"
icon="hass:plus"
on-click="_addItem"
></ha-icon-button>
<paper-item-body>
<paper-input
id="addBox"
placeholder="[[localize('ui.panel.shopping-list.add_item')]]"
on-keydown="_addKeyPress"
no-label-float
></paper-input>
</paper-item-body>
</paper-icon-item>
<template is="dom-repeat" items="[[items]]">
<paper-icon-item>
<paper-checkbox
slot="item-icon"
checked="{{item.complete}}"
on-click="_itemCompleteTapped"
tabindex="0"
></paper-checkbox>
<paper-item-body>
<paper-input
id="editBox"
no-label-float
value="[[item.name]]"
on-change="_saveEdit"
></paper-input>
</paper-item-body>
</paper-icon-item>
</template>
</ha-card>
<div class="tip" hidden$="[[!conversation]]">
[[localize('ui.panel.shopping-list.microphone_tip')]]
</div>
</div>
</ha-app-layout>
`;
}
static get properties() {
return {
hass: Object,
narrow: Boolean,
conversation: {
type: Boolean,
computed: "_computeConversation(hass)",
},
items: {
type: Array,
value: [],
},
};
}
connectedCallback() {
super.connectedCallback();
this._fetchData = this._fetchData.bind(this);
this.hass.connection
.subscribeEvents(this._fetchData, "shopping_list_updated")
.then(
function (unsub) {
this._unsubEvents = unsub;
}.bind(this)
);
this._fetchData();
}
disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubEvents) this._unsubEvents();
}
_fetchData() {
this.hass.callApi("get", "shopping_list").then(
function (items) {
items.reverse();
this.items = items;
}.bind(this)
);
}
_itemCompleteTapped(ev) {
ev.stopPropagation();
this.hass
.callApi("post", "shopping_list/item/" + ev.model.item.id, {
complete: ev.target.checked,
})
.catch(() => this._fetchData());
}
_addItem(ev) {
this.hass
.callApi("post", "shopping_list/item", {
name: this.$.addBox.value,
})
.catch(() => this._fetchData());
this.$.addBox.value = "";
// Presence of 'ev' means tap on "add" button.
if (ev) {
setTimeout(() => this.$.addBox.focus(), 10);
}
}
_addKeyPress(ev) {
if (ev.keyCode === 13) {
this._addItem();
}
}
_computeConversation(hass) {
return isComponentLoaded(hass, "conversation");
}
_showVoiceCommandDialog() {
showVoiceCommandDialog(this);
}
_saveEdit(ev) {
const { index, item } = ev.model;
const name = ev.target.value;
if (name === item.name) {
return;
}
this.set(["items", index, "name"], name);
this.hass
.callApi("post", "shopping_list/item/" + item.id, {
name: name,
})
.catch(() => this._fetchData());
}
_clearCompleted() {
this.hass.callApi("POST", "shopping_list/clear_completed");
}
}
customElements.define("ha-panel-shopping-list", HaPanelShoppingList);

View File

@ -0,0 +1,130 @@
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import {
css,
CSSResultArray,
customElement,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { mdiMicrophone } from "@mdi/js";
import { HomeAssistant } from "../../types";
import { haStyle } from "../../resources/styles";
import { createCardElement } from "../lovelace/create-element/create-card-element";
import { LovelaceCard } from "../lovelace/types";
import { HuiErrorCard } from "../lovelace/cards/hui-error-card";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import "../../components/ha-menu-button";
import "../../layouts/ha-app-layout";
@customElement("ha-panel-shopping-list")
class PanelShoppingList extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
@internalProperty() private _card!: LovelaceCard | HuiErrorCard;
private _conversation = memoizeOne((_components) =>
isComponentLoaded(this.hass, "conversation")
);
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
this._card = createCardElement({ type: "shopping-list" }) as LovelaceCard;
this._card.hass = this.hass;
}
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (changedProperties.has("hass")) {
this._card.hass = this.hass;
}
}
protected render(): TemplateResult {
return html`
<ha-app-layout>
<app-header fixed slot="header">
<app-toolbar>
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
<div main-title>${this.hass.localize("panel.shopping_list")}</div>
${this._conversation(this.hass.config.components)
? html`
<mwc-icon-button
.label=${this.hass!.localize(
"ui.panel.shopping_list.start_conversation"
)}
@click=${this._showVoiceCommandDialog}
>
<ha-svg-icon .path=${mdiMicrophone}></ha-svg-icon>
</mwc-icon-button>
`
: ""}
</app-toolbar>
</app-header>
<div id="columns">
<div class="column">
${this._card}
</div>
</div>
</ha-app-layout>
`;
}
private _showVoiceCommandDialog(): void {
showVoiceCommandDialog(this);
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
:host {
--mdc-theme-primary: var(--app-header-text-color);
display: block;
height: 100%;
}
:host([narrow]) app-toolbar mwc-button {
width: 65px;
}
.heading {
overflow: hidden;
white-space: nowrap;
margin-top: 4px;
}
#columns {
display: flex;
flex-direction: row;
justify-content: center;
margin-left: 4px;
margin-right: 4px;
}
.column {
flex: 1 0 0;
max-width: 500px;
min-width: 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-panel-shopping-list": PanelShoppingList;
}
}

View File

@ -7,7 +7,7 @@
"logbook": "Logbook",
"history": "History",
"mailbox": "Mailbox",
"shopping_list": "Shopping list",
"shopping_list": "Shopping List",
"developer_tools": "Developer Tools",
"media_browser": "Media Browser",
"profile": "Profile"
@ -2790,10 +2790,8 @@
"empty_state": "You have no long-lived access tokens yet."
}
},
"shopping-list": {
"clear_completed": "Clear completed",
"add_item": "Add item",
"microphone_tip": "Tap the microphone on the top right and say or type “Add candy to my shopping list”"
"shopping_list": {
"start_conversation": "Start conversation"
},
"page-authorize": {
"initializing": "Initializing",