mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Use shopping list card in panel (#7519)
This commit is contained in:
parent
1c9d0200ca
commit
9a3a7c28f4
@ -13,7 +13,6 @@
|
|||||||
"src/panels/iframe/ha-panel-iframe.js",
|
"src/panels/iframe/ha-panel-iframe.js",
|
||||||
"src/panels/logbook/ha-panel-logbook.js",
|
"src/panels/logbook/ha-panel-logbook.js",
|
||||||
"src/panels/map/ha-panel-map.js",
|
"src/panels/map/ha-panel-map.js",
|
||||||
"src/panels/shopping-list/ha-panel-shopping-list.js",
|
|
||||||
"src/panels/mailbox/ha-panel-mailbox.js",
|
"src/panels/mailbox/ha-panel-mailbox.js",
|
||||||
"hassio/src/entrypoint.js"
|
"hassio/src/entrypoint.js"
|
||||||
],
|
],
|
||||||
|
@ -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);
|
|
130
src/panels/shopping-list/ha-panel-shopping-list.ts
Normal file
130
src/panels/shopping-list/ha-panel-shopping-list.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
"logbook": "Logbook",
|
"logbook": "Logbook",
|
||||||
"history": "History",
|
"history": "History",
|
||||||
"mailbox": "Mailbox",
|
"mailbox": "Mailbox",
|
||||||
"shopping_list": "Shopping list",
|
"shopping_list": "Shopping List",
|
||||||
"developer_tools": "Developer Tools",
|
"developer_tools": "Developer Tools",
|
||||||
"media_browser": "Media Browser",
|
"media_browser": "Media Browser",
|
||||||
"profile": "Profile"
|
"profile": "Profile"
|
||||||
@ -2790,10 +2790,8 @@
|
|||||||
"empty_state": "You have no long-lived access tokens yet."
|
"empty_state": "You have no long-lived access tokens yet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shopping-list": {
|
"shopping_list": {
|
||||||
"clear_completed": "Clear completed",
|
"start_conversation": "Start conversation"
|
||||||
"add_item": "Add item",
|
|
||||||
"microphone_tip": "Tap the microphone on the top right and say or type “Add candy to my shopping list”"
|
|
||||||
},
|
},
|
||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Initializing",
|
"initializing": "Initializing",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user