From 5866072aa7e8c4f05b7f787d27d7b3e30cf565aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com>
Date: Thu, 24 Oct 2024 17:32:46 -0500
Subject: [PATCH] feat: search
---
.../website/src/components/Gallery/List.tsx | 48 +++++++++++++++++++
.../website/src/components/Gallery/Search.tsx | 26 ++++++++++
.../TankSearch/components/SearchBar.tsx | 2 +-
.../src/pages/tools/gallery/_index.tsx | 16 +++++++
.../src/pages/tools/gallery/index.astro | 41 +++++-----------
.../website/src/stores/galleryEphemeral.ts | 16 +++++++
6 files changed, 119 insertions(+), 30 deletions(-)
create mode 100644 packages/website/src/components/Gallery/List.tsx
create mode 100644 packages/website/src/components/Gallery/Search.tsx
create mode 100644 packages/website/src/pages/tools/gallery/_index.tsx
create mode 100644 packages/website/src/stores/galleryEphemeral.ts
diff --git a/packages/website/src/components/Gallery/List.tsx b/packages/website/src/components/Gallery/List.tsx
new file mode 100644
index 000000000..066b63cf9
--- /dev/null
+++ b/packages/website/src/components/Gallery/List.tsx
@@ -0,0 +1,48 @@
+import { Flex, Text } from '@radix-ui/themes';
+import fuzzysort from 'fuzzysort';
+import { useMemo } from 'react';
+import { GalleryEphemeral } from '../../stores/galleryEphemeral';
+
+interface GalleryListProps {
+ avatars: Avatar[];
+}
+
+export interface Avatar {
+ name: string;
+ id: string;
+}
+
+export function GalleryList({ avatars }: GalleryListProps) {
+ const search = GalleryEphemeral.use((state) => state.search);
+ const filtered = useMemo(() => {
+ if (search === undefined) {
+ return avatars;
+ } else {
+ return fuzzysort
+ .go(search, avatars, { key: 'name' })
+ .map((result) => result.obj);
+ }
+ }, [search]);
+
+ return (
+
+ {filtered.map((avatar) => {
+ return (
+
+
+
+ {avatar.name}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/packages/website/src/components/Gallery/Search.tsx b/packages/website/src/components/Gallery/Search.tsx
new file mode 100644
index 000000000..43ac28fd0
--- /dev/null
+++ b/packages/website/src/components/Gallery/Search.tsx
@@ -0,0 +1,26 @@
+import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
+import { TextField } from '@radix-ui/themes';
+import { useRef } from 'react';
+import { GalleryEphemeral } from '../../stores/galleryEphemeral';
+
+export function GallerySearch() {
+ const input = useRef(null);
+ const mutateGalleryEphemeral = GalleryEphemeral.useMutation();
+
+ return (
+ {
+ mutateGalleryEphemeral((draft) => {
+ const trimmed = event.target.value.trim();
+ draft.search = trimmed.length === 0 ? undefined : trimmed;
+ });
+ }}
+ >
+
+
+
+
+ );
+}
diff --git a/packages/website/src/components/TankSearch/components/SearchBar.tsx b/packages/website/src/components/TankSearch/components/SearchBar.tsx
index e3d0efbbe..d06c0d09f 100644
--- a/packages/website/src/components/TankSearch/components/SearchBar.tsx
+++ b/packages/website/src/components/TankSearch/components/SearchBar.tsx
@@ -61,7 +61,7 @@ export function SearchBar({ topResult, onSelect }: SearchBarProps) {
);
return (
-
+
+
+
+
+ );
+}
diff --git a/packages/website/src/pages/tools/gallery/index.astro b/packages/website/src/pages/tools/gallery/index.astro
index 1b317cac9..f49a17430 100644
--- a/packages/website/src/pages/tools/gallery/index.astro
+++ b/packages/website/src/pages/tools/gallery/index.astro
@@ -1,45 +1,28 @@
---
-import { assertSecret, type BlitzGlossary } from '@blitzkit/core';
-import { Flex, Text } from '@radix-ui/themes';
+import type { Avatar } from '../../../components/Gallery/List';
import { PageWrapper } from '../../../components/PageWrapper';
import { galleryTool } from '../../../constants/tools';
+import { fetchGlossary } from '../../../core/blitz/fetchGlossary';
import ToolLayout from '../../../layouts/ToolLayout.astro';
+import { Page } from './_index';
-const response = await fetch(assertSecret(import.meta.env.WOTB_GLOSSARY), {
- cache: 'force-cache',
-});
-const avatars = Object.entries((await response.json()) as BlitzGlossary)
+const glossary = await fetchGlossary();
+const avatars = Object.entries(glossary)
.filter(
([key, value]) =>
key.startsWith('avatar') &&
!key.endsWith('_part') &&
value.image_url !== null,
)
- .sort(([, a], [, b]) => a.title.localeCompare(b.title));
+ .map(
+ ([key, value]) =>
+ ({ id: key.replace('avatar_', ''), name: value.title }) satisfies Avatar,
+ )
+ .sort((a, b) => a.name.localeCompare(b.name));
---
-
-
- {
- avatars.map(([key, value]) => {
- return (
-
-
-
- {value.title}
-
-
- );
- })
- }
-
+
+
diff --git a/packages/website/src/stores/galleryEphemeral.ts b/packages/website/src/stores/galleryEphemeral.ts
new file mode 100644
index 000000000..54e01999f
--- /dev/null
+++ b/packages/website/src/stores/galleryEphemeral.ts
@@ -0,0 +1,16 @@
+import { create } from 'zustand';
+import { subscribeWithSelector } from 'zustand/middleware';
+import { createContextualStore } from '../core/zustand/createContextualStore';
+
+interface GalleryEphemeral {
+ searching: boolean;
+ search?: string;
+}
+
+export const GalleryEphemeral = createContextualStore(() => {
+ return create()(
+ subscribeWithSelector(() => ({
+ searching: false,
+ })),
+ );
+});