mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +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/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"
|
||||
],
|
||||
|
@ -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",
|
||||
"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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user