From a89f0bd1cd08abc05ba467e3e1a2da576ceb9917 Mon Sep 17 00:00:00 2001 From: Timmo Date: Sun, 12 May 2019 10:13:16 +0100 Subject: [PATCH] Add Search to Hassio add-on store (#3108) * :sparkles: Add search to hassio add-ons * :shirt: Fix linter error * :shirt: Lint fixes * :fire: Remove search from dashboard for this PR * :fire: Remove search from dasboard in this PR * :hammer: Suggested changes * :hammer: Change to fireEvent * :hammer: Convert definition * :fire: Fix imports * :fire: Revert styling test * :hammer: Fix search * :hammer: CSS fix * :hammer: Add smaller message to show no results found in repo * :hammer: Fixes * :hammer: CSS fixes * :hammer: Add types * :art: Max width * :hammer: Fix margin jump * :hammer: Add working memoizeOne * :shirt: Fix linting / error on build --- .../addon-store/hassio-addon-repository.ts | 73 +++++++++++------ hassio/src/addon-store/hassio-addon-store.ts | 13 +++ hassio/src/components/hassio-filter-addons.ts | 13 +++ hassio/src/components/hassio-search-input.ts | 82 +++++++++++++++++++ package.json | 1 + yarn.lock | 5 ++ 6 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 hassio/src/components/hassio-filter-addons.ts create mode 100644 hassio/src/components/hassio-search-input.ts diff --git a/hassio/src/addon-store/hassio-addon-repository.ts b/hassio/src/addon-store/hassio-addon-repository.ts index dd5a56ea46..5427ae8fe1 100644 --- a/hassio/src/addon-store/hassio-addon-repository.ts +++ b/hassio/src/addon-store/hassio-addon-repository.ts @@ -7,6 +7,7 @@ import { CSSResultArray, } from "lit-element"; import "@polymer/paper-card/paper-card"; +import memoizeOne from "memoize-one"; import "../components/hassio-card-content"; import { hassioStyle } from "../resources/hassio-style"; @@ -16,14 +17,40 @@ import { HassioAddonRepository, } from "../../../src/data/hassio"; import { navigate } from "../../../src/common/navigate"; +import { filterAndSort } from "../components/hassio-filter-addons"; class HassioAddonRepositoryEl extends LitElement { @property() public hass!: HomeAssistant; @property() public repo!: HassioAddonRepository; @property() public addons!: HassioAddonInfo[]; + @property() public filter!: string; + + private _getAddons = memoizeOne( + (addons: HassioAddonInfo[], filter?: string) => { + if (filter) { + return filterAndSort(addons, filter); + } + return addons.sort((a, b) => + a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1 + ); + } + ); protected render(): TemplateResult | void { const repo = this.repo; + const addons = this._getAddons(this.addons, this.filter); + + if (this.filter && addons.length < 1) { + return html` +
+
+
+ No results found in "${repo.name}" +
+
+
+ `; + } return html`
@@ -34,31 +61,27 @@ class HassioAddonRepositoryEl extends LitElement {
- ${this.addons - .sort((a, b) => - a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1 - ) - .map( - (addon) => html` - -
- -
-
- ` - )} + ${addons.map( + (addon) => html` + +
+ +
+
+ ` + )} `; } diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index 35440bb58e..2035a5b16f 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -16,6 +16,7 @@ import { reloadHassioAddons, } from "../../../src/data/hassio"; import "../../../src/layouts/loading-screen"; +import "../components/hassio-search-input"; const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => { if (a.slug === "local") { @@ -37,10 +38,12 @@ class HassioAddonStore extends LitElement { @property() public hass!: HomeAssistant; @property() private _addons?: HassioAddonInfo[]; @property() private _repos?: HassioAddonRepository[]; + @property() private _filter?: string; public async refreshData() { this._repos = undefined; this._addons = undefined; + this._filter = undefined; await reloadHassioAddons(this.hass); await this._loadData(); } @@ -67,6 +70,7 @@ class HassioAddonStore extends LitElement { .hass=${this.hass} .repo=${repo} .addons=${addons} + .filter=${this._filter} > `); } @@ -77,6 +81,11 @@ class HassioAddonStore extends LitElement { .repos=${this._repos} > + + ${repos} `; } @@ -104,6 +113,10 @@ class HassioAddonStore extends LitElement { } } + private async _filterChanged(e) { + this._filter = e.detail.value; + } + static get styles(): CSSResult { return css` hassio-addon-repository { diff --git a/hassio/src/components/hassio-filter-addons.ts b/hassio/src/components/hassio-filter-addons.ts new file mode 100644 index 0000000000..67bd0d4093 --- /dev/null +++ b/hassio/src/components/hassio-filter-addons.ts @@ -0,0 +1,13 @@ +import { HassioAddonInfo } from "../../../src/data/hassio"; +import * as Fuse from "fuse.js"; + +export function filterAndSort(addons: HassioAddonInfo[], filter: string) { + const options: Fuse.FuseOptions = { + keys: ["name", "description", "slug"], + caseSensitive: false, + minMatchCharLength: 2, + threshold: 0.2, + }; + const fuse = new Fuse(addons, options); + return fuse.search(filter); +} diff --git a/hassio/src/components/hassio-search-input.ts b/hassio/src/components/hassio-search-input.ts new file mode 100644 index 0000000000..11a43eb4d1 --- /dev/null +++ b/hassio/src/components/hassio-search-input.ts @@ -0,0 +1,82 @@ +import { TemplateResult, html } from "lit-html"; +import { + css, + CSSResult, + customElement, + LitElement, + property, +} from "lit-element"; +import { fireEvent } from "../../../src/common/dom/fire_event"; +import "@polymer/iron-icon/iron-icon"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@material/mwc-button"; + +@customElement("hassio-search-input") +class HassioSearchInput extends LitElement { + @property() private filter?: string; + + protected render(): TemplateResult | void { + return html` +
+ + + ${this.filter && + html` + + `} + +
+ `; + } + + private async _filterChanged(value: string) { + fireEvent(this, "value-changed", { value: String(value) }); + } + + private async _filterInputChanged(e) { + this._filterChanged(e.target.value); + } + + private async _clearSearch() { + this._filterChanged(""); + } + + static get styles(): CSSResult { + return css` + paper-input { + flex: 1 1 auto; + margin: 0 16px; + } + .search-container { + display: inline-flex; + width: 100%; + align-items: center; + } + .prefix { + margin: 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hassio-search-input": HassioSearchInput; + } +} diff --git a/package.json b/package.json index edaa0308dc..22c68baa9b 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "deep-clone-simple": "^1.1.1", "es6-object-assign": "^1.1.0", "fecha": "^3.0.2", + "fuse.js": "^3.4.4", "hls.js": "^0.12.4", "home-assistant-js-websocket": "^4.1.2", "intl-messageformat": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index 42d45f0325..f35c71ba6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6489,6 +6489,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +fuse.js@^3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.4.tgz#f98f55fcb3b595cf6a3e629c5ffaf10982103e95" + integrity sha512-pyLQo/1oR5Ywf+a/tY8z4JygnIglmRxVUOiyFAbd11o9keUDpUJSMGRWJngcnkURj30kDHPmhoKY8ChJiz3EpQ== + g-status@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97"