mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-05 19:50:32 +08:00
Make search bar more interactive
Signed-off-by: Rūdolfs Ošiņš <rudolfs@osins.org>
This commit is contained in:
+1
-10
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,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("/");
|
||||
|
||||
@@ -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)}`;
|
||||
|
||||
Reference in New Issue
Block a user