mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 23:06:40 +00:00
New Card: Shopping List (#1970)
* New Card: Shopping List Following features: - Add item - Edit item - Complete item - Clear items * Address Travis complaint * Addressed review comments * Update translation variable name * Line up input row text * Taking MVP to heart Addressed review comments and scaled this back to just get a simple shopping list card out there and we can discuss/debate how best to add the additional pieces with smaller PRs * Remove calling connected in set hass
This commit is contained in:
parent
935639e5e0
commit
6432207bf1
@ -29,6 +29,12 @@ export default (elements, { initialStates = {} } = {}) => {
|
||||
resources: demoResources,
|
||||
states: initialStates,
|
||||
themes: {},
|
||||
connection: {
|
||||
subscribeEvents: async (callback, event) => {
|
||||
console.log("subscribeEvents", event);
|
||||
return () => console.log("unsubscribeEvents", event);
|
||||
},
|
||||
},
|
||||
|
||||
// Mock properties
|
||||
mockEntities: entities,
|
||||
|
55
gallery/src/demos/demo-hui-shopping-list-card.js
Normal file
55
gallery/src/demos/demo-hui-shopping-list-card.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import provideHass from "../data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "List example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "List with title example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
title: Shopping List
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
class DemoShoppingListEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id='demos'
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
|
||||
hass.mockAPI("shopping_list", () => [
|
||||
{ name: "list", id: 1, complete: false },
|
||||
{ name: "all", id: 2, complete: false },
|
||||
{ name: "the", id: 3, complete: false },
|
||||
{ name: "things", id: 4, complete: true },
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
|
28
src/data/shopping-list.ts
Normal file
28
src/data/shopping-list.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export interface ShoppingListItem {
|
||||
id: number;
|
||||
name: string;
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
export const fetchItems = (hass: HomeAssistant): Promise<ShoppingListItem[]> =>
|
||||
hass.callApi("GET", "shopping_list");
|
||||
|
||||
export const saveEdit = (
|
||||
hass: HomeAssistant,
|
||||
itemId: number,
|
||||
name: string
|
||||
): Promise<ShoppingListItem> =>
|
||||
hass.callApi("POST", "shopping_list/item/" + itemId, {
|
||||
name,
|
||||
});
|
||||
|
||||
export const completeItem = (
|
||||
hass: HomeAssistant,
|
||||
itemId: number,
|
||||
complete: boolean
|
||||
): Promise<void> =>
|
||||
hass.callApi("POST", "shopping_list/item/" + itemId, {
|
||||
complete,
|
||||
});
|
165
src/panels/lovelace/cards/hui-shopping-list-card.ts
Normal file
165
src/panels/lovelace/cards/hui-shopping-list-card.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { html, LitElement } from "@polymer/lit-element";
|
||||
import { repeat } from "lit-html/directives/repeat";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-checkbox/paper-checkbox";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||
import {
|
||||
fetchItems,
|
||||
completeItem,
|
||||
saveEdit,
|
||||
ShoppingListItem,
|
||||
} from "../../../data/shopping-list";
|
||||
|
||||
interface Config extends LovelaceConfig {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
class HuiShoppingListCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
private _hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _items?: ShoppingListItem[];
|
||||
private _unsubEvents?: Promise<() => Promise<void>>;
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_config: {},
|
||||
_items: {},
|
||||
};
|
||||
}
|
||||
|
||||
set hass(hass: HomeAssistant) {
|
||||
this._hass = hass;
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return (
|
||||
(this._config ? (this._config.title ? 1 : 0) : 0) +
|
||||
(this._items ? this._items.length : 3)
|
||||
);
|
||||
}
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
this._config = config;
|
||||
this._items = [];
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
if (this._hass) {
|
||||
this._unsubEvents = this._hass.connection.subscribeEvents(
|
||||
() => this._fetchData(),
|
||||
"shopping_list_updated"
|
||||
);
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
|
||||
if (this._unsubEvents) {
|
||||
this._unsubEvents.then((unsub) => unsub());
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this._hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-card .header="${this._config.title}">
|
||||
${repeat(
|
||||
this._items!,
|
||||
(item) => item.id,
|
||||
(item, index) =>
|
||||
html`
|
||||
<div class="editRow">
|
||||
<paper-checkbox
|
||||
slot="item-icon"
|
||||
id=${index}
|
||||
?checked=${item.complete}
|
||||
.itemId=${item.id}
|
||||
@click=${this._completeItem}
|
||||
tabindex='0'
|
||||
></paper-checkbox>
|
||||
<paper-item-body>
|
||||
<paper-input
|
||||
no-label-float
|
||||
.value='${item.name}'
|
||||
.itemId=${item.id}
|
||||
@change=${this._saveEdit}
|
||||
></paper-input>
|
||||
</paper-item-body>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.editRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
paper-checkbox {
|
||||
padding: 11px 11px 11px 18px;
|
||||
}
|
||||
paper-input {
|
||||
--paper-input-container-underline: {
|
||||
display: none;
|
||||
}
|
||||
--paper-input-container-underline-focus: {
|
||||
display: none;
|
||||
}
|
||||
--paper-input-container-underline-disabled: {
|
||||
display: none;
|
||||
}
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._hass) {
|
||||
this._items = await fetchItems(this._hass);
|
||||
}
|
||||
}
|
||||
|
||||
private _completeItem(ev): void {
|
||||
completeItem(this._hass!, ev.target.itemId, ev.target.checked).catch(() =>
|
||||
this._fetchData()
|
||||
);
|
||||
}
|
||||
|
||||
private _saveEdit(ev): void {
|
||||
saveEdit(this._hass!, ev.target.itemId, ev.target.value).catch(() =>
|
||||
this._fetchData()
|
||||
);
|
||||
|
||||
ev.target.blur();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-shopping-list-card": HuiShoppingListCard;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-shopping-list-card", HuiShoppingListCard);
|
@ -21,6 +21,7 @@ import "../cards/hui-picture-glance-card";
|
||||
import "../cards/hui-plant-status-card";
|
||||
import "../cards/hui-sensor-card";
|
||||
import "../cards/hui-vertical-stack-card.ts";
|
||||
import "../cards/hui-shopping-list-card";
|
||||
import "../cards/hui-thermostat-card.ts";
|
||||
import "../cards/hui-weather-forecast-card";
|
||||
import "../cards/hui-gauge-card";
|
||||
@ -49,6 +50,7 @@ const CARD_TYPES = new Set([
|
||||
"picture-glance",
|
||||
"plant-status",
|
||||
"sensor",
|
||||
"shopping-list",
|
||||
"thermostat",
|
||||
"vertical-stack",
|
||||
"weather-forecast",
|
||||
|
Loading…
x
Reference in New Issue
Block a user