Make search bar more interactive

Signed-off-by: Rūdolfs Ošiņš <rudolfs@osins.org>
This commit is contained in:
Rūdolfs Ošiņš
2023-02-13 11:28:42 +01:00
parent b8b203514b
commit 5cbf8b97da
7 changed files with 77 additions and 26 deletions
+1 -10
View File
@@ -29,19 +29,12 @@
display: flex;
height: var(--button-regular-height);
align-items: center;
margin-right: 0.5rem;
}
.search {
width: 16rem;
}
@media (max-width: 720px) {
header .right {
gap: 1rem;
}
.search {
display: none;
}
}
.toggle {
@@ -65,9 +58,7 @@
<header>
<div class="left">
<Link route={{ resource: "home" }}><span class="logo"><Logo /></span></Link>
<div class="search">
<Search />
</div>
<Search />
</div>
<div class="right">
+35 -12
View File
@@ -1,25 +1,30 @@
<script lang="ts" strictEvents>
import debounce from "lodash/debounce";
import { createEventDispatcher } from "svelte";
import * as router from "@app/lib/router";
import * as Search from "@app/lib/search";
import * as modal from "@app/lib/modal";
import * as router from "@app/lib/router";
import { unreachable } from "@app/lib/utils";
import Icon from "@app/components/Icon.svelte";
import SearchResultsModal from "@app/App/Header/SearchResultsModal.svelte";
import TextInput from "@app/components/TextInput.svelte";
import { unreachable } from "@app/lib/utils";
const dispatch = createEventDispatcher<{
finished: never;
}>();
let input = "";
export let input = "";
let loading = false;
let shaking = false;
// Clears search input on user navigation.
router.historyStore.subscribe(() => (input = ""));
const collapsedWidth = 13;
const expandedWidth = 20;
function shake() {
shaking = true;
debounce(() => (shaking = false), 500)();
@@ -64,6 +69,7 @@
});
}
dispatch("finished");
searchBarWidth = collapsedWidth;
} else {
unreachable(searchResult);
}
@@ -71,6 +77,7 @@
}
$: valid = input !== "";
$: searchBarWidth = collapsedWidth;
</script>
<style>
@@ -97,15 +104,31 @@
transform: translateX(0);
}
}
.search {
transition: all 0.2s;
}
@media (max-width: 720px) {
.search {
display: none;
}
}
</style>
<div class="search-bar" class:shaking>
<TextInput
variant="dashed"
valid={input !== ""}
{loading}
disabled={loading}
bind:value={input}
on:submit={search}
placeholder="Search a name or address…" />
<div class="search" style:width={`${searchBarWidth}rem`}>
<div class="search-bar" class:shaking>
<TextInput
valid={input !== ""}
{loading}
disabled={loading}
bind:value={input}
on:focus={() => (searchBarWidth = expandedWidth)}
on:blur={() =>
input === "" ? (searchBarWidth = collapsedWidth) : undefined}
on:submit={search}
placeholder="Search a name…">
<div slot="left">
<Icon name="magnifying-glass" />
</div>
</TextInput>
</div>
</div>
+1 -1
View File
@@ -17,7 +17,7 @@
case "/": {
event.preventDefault();
const searchInput: HTMLElement | null = document.querySelector(
'*[placeholder="Search a name or address…"]',
'*[placeholder="Search a name…"]',
);
searchInput?.focus();
break;
+13
View File
@@ -12,6 +12,7 @@
| "ellipsis"
| "fork"
| "gear"
| "magnifying-glass"
| "moon"
| "sun"
| "twitter";
@@ -297,6 +298,18 @@
1.8701-0.84393 3.4204-0.22929 4.6486 0.6925 1.3838 2.004 2.4102 3.1211 3.0036
0.15696 0.08337 0.23697 0.26295 0.19401 0.43534-0.042953 0.17246-0.19784
0.29353-0.37557 0.29353z" />
{:else if name === "magnifying-glass"}
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11 6.5C8.51472 6.5 6.5
8.51472 6.5 11C6.5 13.4853 8.51472 15.5 11 15.5C13.4853 15.5 15.5 13.4853
15.5 11C15.5 8.51472 13.4853 6.5 11 6.5ZM5.5 11C5.5 7.96243 7.96243 5.5 11
5.5C14.0376 5.5 16.5 7.96243 16.5 11C16.5 12.3387 16.0217 13.5658 15.2266
14.5195L19.3536 18.6464C19.5488 18.8417 19.5488 19.1583 19.3536
19.3536C19.1583 19.5488 18.8417 19.5488 18.6464 19.3536L14.5195
15.2266C13.5658 16.0217 12.3387 16.5 11 16.5C7.96243 16.5 5.5 14.0376 5.5
11Z" />
{:else}
{unreachable(name)}
{/if}
+24
View File
@@ -21,6 +21,7 @@
}>();
let rightContainerWidth: number;
let leftContainerWidth: number;
let inputElement: HTMLInputElement | undefined = undefined;
onMount(() => {
@@ -84,6 +85,18 @@
.dashed:focus {
background: var(--color-background-1);
}
.left-container {
color: var(--color-secondary);
position: absolute;
left: 0;
top: 0;
display: flex;
align-items: center;
height: var(--button-regular-height);
padding-right: 0.5rem;
padding-left: 0.5rem;
gap: 0.5rem;
}
.right-container {
color: var(--color-secondary);
position: absolute;
@@ -120,9 +133,18 @@
<div class="wrapper">
<div class="validation-wrapper">
<div class="left-container" bind:clientWidth={leftContainerWidth}>
{#if $$slots.left}
<slot name="left" />
{/if}
</div>
<input
class:regular={variant === "regular"}
class:dashed={variant === "dashed"}
style:padding-left={leftContainerWidth
? `${leftContainerWidth}px`
: "auto"}
style:padding-right={rightContainerWidth
? `${rightContainerWidth}px`
: "auto"}
@@ -133,6 +155,8 @@
{disabled}
bind:value
on:input
on:focus
on:blur
on:keydown|stopPropagation={handleKeydown}
on:click
on:change />
+1 -1
View File
@@ -1,6 +1,6 @@
import { test, expect } from "@tests/support/fixtures.js";
const searchPlaceholder = "Search a name or address…";
const searchPlaceholder = "Search a name…";
test("global hotkeys", async ({ page }) => {
await page.goto("/");
+2 -2
View File
@@ -8,7 +8,7 @@ import {
test("navigate to existing project", async ({ page }) => {
await page.goto("/");
const searchInput = page.getByPlaceholder("Search a name or address…");
const searchInput = page.getByPlaceholder("Search a name…");
await searchInput.click();
await searchInput.fill(`${ridPrefix}${rid}`);
await searchInput.press("Enter");
@@ -19,7 +19,7 @@ test("navigate to existing project", async ({ page }) => {
test("navigate to a project that does not exist", async ({ page }) => {
await page.goto("/");
const searchInput = page.getByPlaceholder("Search a name or address…");
const searchInput = page.getByPlaceholder("Search a name…");
await searchInput.click();
const nonExistantId = `${ridPrefix}:zt${rid.substring(2)}`;