From 41f0432e8d97422d7aada60189683759e9a0af1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Cocchi?= Date: Sun, 6 Feb 2022 12:17:29 +0100 Subject: [PATCH 1/9] handle resetting search field --- web/res/reset.svg | 1 + web/src/index.js | 40 ++++++++++++++++++++++------------------ web/src/search-box.js | 17 ++++++++++++----- web/style/index.sass | 5 ++++- 4 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 web/res/reset.svg diff --git a/web/res/reset.svg b/web/res/reset.svg new file mode 100644 index 0000000..be0a47f --- /dev/null +++ b/web/res/reset.svg @@ -0,0 +1 @@ + diff --git a/web/src/index.js b/web/src/index.js index a215c46..46d9bf6 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -41,10 +41,7 @@ const supportedThemes = ["light", "dark", "black"] const defaultState = { packs: [], - filtering: { - searchTerm: "", - packs: [], - }, + searchTerm: null, } class App extends Component { @@ -52,7 +49,7 @@ class App extends Component { super(props) this.defaultTheme = params.get("theme") this.state = { - packs: defaultState.packs, + ...defaultState, loading: true, error: null, stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"), @@ -63,7 +60,6 @@ class App extends Component { stickerIDs: frequent.get(), stickers: [], }, - filtering: defaultState.filtering, } if (!supportedThemes.includes(this.state.theme)) { this.state.theme = "light" @@ -77,6 +73,7 @@ class App extends Component { this.packListRef = null this.navRef = null this.searchStickers = this.searchStickers.bind(this) + this.resetSearch = this.resetSearch.bind(this) this.sendSticker = this.sendSticker.bind(this) this.navScroll = this.navScroll.bind(this) this.reloadPacks = this.reloadPacks.bind(this) @@ -101,7 +98,18 @@ class App extends Component { localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker])) } + // Search + + resetSearch() { + this.setState({ searchTerm: defaultState.searchTerm }) + } + searchStickers(e) { + if (e.key === "Escape") { + this.resetSearch() + return + } + const sanitizeString = s => s.toLowerCase().trim() const searchTerm = sanitizeString(e.target.value) @@ -115,11 +123,8 @@ class App extends Component { })) this.setState({ - filtering: { - ...this.state.filtering, - searchTerm, - packs: packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length), - }, + filteredPacks: packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length), + searchTerm, }) } @@ -145,10 +150,8 @@ class App extends Component { reloadPacks() { this.imageObserver.disconnect() this.sectionObserver.disconnect() - this.setState({ - packs: defaultState.packs, - filtering: defaultState.filtering, - }) + this.setState({ packs: defaultState.packs }) + this.resetSearch() this._loadPacks(true) } @@ -258,6 +261,7 @@ class App extends Component { const sticker = this.stickersByID.get(id) frequent.add(id) this.updateFrequentlyUsed() + this.resetSearch() widgetAPI.sendSticker(sticker) } @@ -267,8 +271,8 @@ class App extends Component { render() { const theme = `theme-${this.state.theme}` - const filterActive = !!this.state.filtering.searchTerm - const packs = filterActive ? this.state.filtering.packs : [this.state.frequentlyUsed, ...this.state.packs] + const filterActive = !!this.state.searchTerm + const packs = filterActive ? this.state.filteredPacks : [this.state.frequentlyUsed, ...this.state.packs] if (this.state.loading) { return html`
<${Spinner} size=${80} green />
` @@ -287,7 +291,7 @@ class App extends Component { ${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack}/>`)} <${NavBarItem} pack=${{ id: "settings", title: "Settings" }} iconOverride="settings" /> - <${SearchBox} onKeyUp=${this.searchStickers} /> + <${SearchBox} onKeyUp=${this.searchStickers} resetSearch=${this.resetSearch} value=${this.state.searchTerm} />
this.packListRef = elem}> ${filterActive && packs.length === 0 ? html`

No stickers match your search

` : null} ${packs.map(pack => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker} />`)} diff --git a/web/src/search-box.js b/web/src/search-box.js index ba2ed5d..5af8f19 100644 --- a/web/src/search-box.js +++ b/web/src/search-box.js @@ -15,12 +15,19 @@ // along with this program. If not, see . import { html } from "../lib/htm/preact.js" -export const SearchBox = ({ onKeyUp, placeholder = 'Find stickers' }) => { - const component = html` +export const SearchBox = ({ onKeyUp, resetSearch, value }) => { + const className = `icon-display ${value ? 'reset-click-zone' : null}` + const title = value ? 'Click to reset' : null + const onClick = value ? resetSearch : null + + const iconToDisplay = `icon-${!value ? 'search' : 'reset'}` + + return html` ` - return component } diff --git a/web/style/index.sass b/web/style/index.sass index c079f65..773c8f9 100644 --- a/web/style/index.sass +++ b/web/style/index.sass @@ -92,9 +92,12 @@ main.theme-black &.icon-recent --icon-image: url(../res/recent.svg) - &.icon.icon-search + &.icon-search --icon-image: url(../res/search.svg) + &.icon-reset + --icon-image: url(../res/reset.svg) + nav display: flex overflow-x: auto From fed4f08218952596d40648bc11495dab3680a573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Cocchi?= Date: Sun, 6 Feb 2022 12:18:11 +0100 Subject: [PATCH 2/9] handle autofocus search + setting on/off --- web/src/index.js | 23 ++++++++++++++++++++++- web/src/search-box.js | 8 +++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/web/src/index.js b/web/src/index.js index 46d9bf6..6307a09 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -15,7 +15,7 @@ // along with this program. If not, see . import { html, render, Component } from "../lib/htm/preact.js" import { Spinner } from "./spinner.js" -import { SearchBox } from "./search-box.js" +import { shouldAutofocusSearchBar, SearchBox } from "./search-box.js" import * as widgetAPI from "./widget-api.js" import * as frequent from "./frequently-used.js" @@ -128,6 +128,10 @@ class App extends Component { }) } + // End search + + // Settings + setStickersPerRow(val) { localStorage.mauStickersPerRow = val document.documentElement.style.setProperty("--stickers-per-row", localStorage.mauStickersPerRow) @@ -147,6 +151,12 @@ class App extends Component { } } + setAutofocusSearchBar(checked) { + localStorage.mauAutofocusSearchBar = checked + } + + // End settings + reloadPacks() { this.imageObserver.disconnect() this.sectionObserver.disconnect() @@ -218,6 +228,9 @@ class App extends Component { for (const entry of intersections) { const packID = entry.target.getAttribute("data-pack-id") const navElement = document.getElementById(`nav-${packID}`) + if (!navElement) { + continue + } if (entry.isIntersecting) { navElement.classList.add("visible") const bb = navElement.getBoundingClientRect() @@ -321,6 +334,14 @@ const Settings = ({ app }) => html`
+
+ Autofocus search bar: + app.setAutofocusSearchBar(evt.target.checked)} + /> +
` diff --git a/web/src/search-box.js b/web/src/search-box.js index 5af8f19..13ae3cf 100644 --- a/web/src/search-box.js +++ b/web/src/search-box.js @@ -15,7 +15,13 @@ // along with this program. If not, see . import { html } from "../lib/htm/preact.js" +export function shouldAutofocusSearchBar() { + return localStorage.mauAutofocusSearchBar === 'true' +} + export const SearchBox = ({ onKeyUp, resetSearch, value }) => { + const autofocus = shouldAutofocusSearchBar() + const className = `icon-display ${value ? 'reset-click-zone' : null}` const title = value ? 'Click to reset' : null const onClick = value ? resetSearch : null @@ -24,7 +30,7 @@ export const SearchBox = ({ onKeyUp, resetSearch, value }) => { return html`