From 9bc252b95038f4c7d11f696e9639fc85dadd9bd6 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Tue, 31 May 2022 22:31:29 +0300 Subject: [PATCH] Add Combobox --- app/components/Combobox.tsx | 58 +++++++++++++++++++ .../suggestions/comment.$tier.$userId.tsx | 9 ++- app/routes/plus/suggestions/new.tsx | 25 +++++--- app/styles/common.css | 25 ++++++++ app/styles/global.css | 2 +- package-lock.json | 14 +++++ package.json | 1 + 7 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 app/components/Combobox.tsx diff --git a/app/components/Combobox.tsx b/app/components/Combobox.tsx new file mode 100644 index 000000000..887ce0fff --- /dev/null +++ b/app/components/Combobox.tsx @@ -0,0 +1,58 @@ +import { Combobox as HeadlessCombobox } from "@headlessui/react"; +import * as React from "react"; +import Fuse from "fuse.js"; +import clsx from "clsx"; + +const MAX_RESULTS_SHOWN = 6; + +export function Combobox({ + options, + onChange, + inputName, + placeholder, +}: { + options: string[] | readonly string[]; + onChange: (value: string) => void; + inputName: string; + placeholder: string; +}) { + const [query, setQuery] = React.useState(""); + + const filteredOptions = (() => { + if (!query) return []; + + const fuse = new Fuse(options); + return fuse + .search(query) + .slice(0, MAX_RESULTS_SHOWN) + .map((res) => res.item); + })(); + + return ( + + setQuery(event.target.value)} + placeholder={placeholder} + className="combobox-input" + name={inputName} + /> + + {filteredOptions.map((option) => ( + + {({ active }) => ( +
  • {option}
  • + )} +
    + ))} +
    +
    + ); +} diff --git a/app/routes/plus/suggestions/comment.$tier.$userId.tsx b/app/routes/plus/suggestions/comment.$tier.$userId.tsx index ec7ca30b1..e425f6ac5 100644 --- a/app/routes/plus/suggestions/comment.$tier.$userId.tsx +++ b/app/routes/plus/suggestions/comment.$tier.$userId.tsx @@ -1,9 +1,8 @@ import type { ActionFunction } from "@remix-run/node"; import { redirect } from "@remix-run/node"; import { Form, useMatches, useNavigate, useParams } from "@remix-run/react"; -import * as React from "react"; import { z } from "zod"; -import { Button } from "~/components/Button"; +import { Button, LinkButton } from "~/components/Button"; import { Dialog } from "~/components/Dialog"; import { Redirect } from "~/components/Redirect"; import { PlUS_SUGGESTION_COMMENT_MAX_LENGTH } from "~/constants"; @@ -96,13 +95,13 @@ export default function PlusCommentModalPage() { - + diff --git a/app/routes/plus/suggestions/new.tsx b/app/routes/plus/suggestions/new.tsx index 6889f4b8d..7443e1302 100644 --- a/app/routes/plus/suggestions/new.tsx +++ b/app/routes/plus/suggestions/new.tsx @@ -1,5 +1,5 @@ -import { useMatches, useNavigate } from "@remix-run/react"; -import { Button } from "~/components/Button"; +import { useMatches } from "@remix-run/react"; +import { Button, LinkButton } from "~/components/Button"; import { Dialog } from "~/components/Dialog"; import { Redirect } from "~/components/Redirect"; import { useUser } from "~/hooks/useUser"; @@ -10,9 +10,9 @@ import * as React from "react"; import { Label } from "~/components/Label"; import { PlUS_SUGGESTION_COMMENT_MAX_LENGTH, PLUS_TIERS } from "~/constants"; import invariant from "tiny-invariant"; +import { Combobox } from "~/components/Combobox"; export default function PlusNewSuggestionModalPage() { - const navigate = useNavigate(); const user = useUser(); const matches = useMatches(); const data = matches.at(-2)!.data as PlusSuggestionsLoaderData; @@ -32,8 +32,8 @@ export default function PlusNewSuggestionModalPage() {

    Adding a new suggestion

    - - {PLUS_TIERS.filter((tier) => { invariant(user?.plusTier); return tier >= user.plusTier; @@ -42,18 +42,27 @@ export default function PlusNewSuggestionModalPage() { ))}
    +
    + + console.log(val)} + inputName="user" + placeholder="Sendou#0043" + /> +
    - +
    diff --git a/app/styles/common.css b/app/styles/common.css index 540cf1eec..e70b77c1c 100644 --- a/app/styles/common.css +++ b/app/styles/common.css @@ -60,6 +60,31 @@ } } +.combobox-input { + width: 12rem; +} + +.combobox-options { + position: absolute; + width: 12rem; + margin-top: var(--s-2); + background-color: var(--bg-darker); + border-radius: var(--rounded); + color: var(--text); + font-size: var(--fonts-sm); + padding-block: var(--s-3); + padding-inline: 0; +} + +.combobox-item { + padding: var(--s-1) var(--s-3); + list-style: none; +} + +.combobox-item.active { + background-color: var(--theme-transparent); +} + .avatar { background-color: var(--bg-lighter); background-image: url("/svg/background-pattern.svg"); diff --git a/app/styles/global.css b/app/styles/global.css index fb62d0d0b..09ee30241 100644 --- a/app/styles/global.css +++ b/app/styles/global.css @@ -303,7 +303,7 @@ select { /* TODO: Get color from CSS var */ background-image: url('data:image/svg+xml;utf8,'); - background-position-x: 95%; + background-position-x: 92%; background-position-y: 50%; background-repeat: no-repeat; border-radius: var(--rounded); diff --git a/package-lock.json b/package-lock.json index 628890a82..4810ced59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "better-sqlite3": "^7.5.3", "clsx": "^1.1.1", "countries-list": "^2.6.1", + "fuse.js": "^6.6.2", "randomcolor": "^0.6.2", "react": "^18.1.0", "react-dom": "^18.1.0", @@ -6714,6 +6715,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", + "engines": { + "node": ">=10" + } + }, "node_modules/gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -19072,6 +19081,11 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, + "fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==" + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", diff --git a/package.json b/package.json index 0d7e13fcc..f54428009 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "better-sqlite3": "^7.5.3", "clsx": "^1.1.1", "countries-list": "^2.6.1", + "fuse.js": "^6.6.2", "randomcolor": "^0.6.2", "react": "^18.1.0", "react-dom": "^18.1.0",