feat: filter select in mii list
This commit is contained in:
parent
7119313b7d
commit
4a15b6d7e3
6 changed files with 63 additions and 14 deletions
53
src/app/components/mii-list/filter-select.tsx
Normal file
53
src/app/components/mii-list/filter-select.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { redirect, useSearchParams } from "next/navigation";
|
||||||
|
import { useState } from "react";
|
||||||
|
import TagSelector from "../tag-selector";
|
||||||
|
|
||||||
|
export default function FilterSelect() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const rawTags = searchParams.get("tags");
|
||||||
|
const preexistingTags = rawTags
|
||||||
|
? rawTags
|
||||||
|
.split(",")
|
||||||
|
.map((tag) => tag.trim())
|
||||||
|
.filter((tag) => tag.length > 0)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [tags, setTags] = useState<string[]>(preexistingTags);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
redirect(`/?tags=${encodeURIComponent(tags.join(","))}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<button onClick={() => setIsOpen((prev) => !prev)} className="pill button gap-1 text-nowrap">
|
||||||
|
Filter{" "}
|
||||||
|
{tags.length > 0 ? (
|
||||||
|
<span>
|
||||||
|
({tags.length} {tags.length == 1 ? "filter" : "filters"})
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`absolute z-40 left-1/2 -translate-x-1/2 w-96 bg-orange-200 border-2 border-orange-400 rounded-lg mt-1 shadow-lg flex flex-col justify-between gap-2 p-2 max-[32rem]:-left-8 max-[32rem]:w-80 max-[32rem]:translate-x-0 ${
|
||||||
|
isOpen ? "block" : "hidden"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm ml-2">Tags</label>
|
||||||
|
<TagSelector tags={tags} setTags={setTags} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onClick={handleSubmit} className="pill button text-sm !px-3 !py-0.5 w-min">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import { prisma } from "@/lib/prisma";
|
||||||
import SortSelect from "./sort-select";
|
import SortSelect from "./sort-select";
|
||||||
import Carousel from "../carousel";
|
import Carousel from "../carousel";
|
||||||
import LikeButton from "../like-button";
|
import LikeButton from "../like-button";
|
||||||
|
import FilterSelect from "./filter-select";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
|
|
@ -94,7 +95,7 @@ export default async function MiiList({ searchParams, userId, where }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex justify-between items-end mb-2">
|
<div className="flex justify-between items-end mb-2 max-[32rem]:flex-col max-[32rem]:items-center">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
{totalMiiCount == shownMiiCount ? (
|
{totalMiiCount == shownMiiCount ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -108,12 +109,7 @@ export default async function MiiList({ searchParams, userId, where }: Props) {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{/* todo: replace with react-select */}
|
<FilterSelect />
|
||||||
<div className="pill gap-2">
|
|
||||||
<label htmlFor="sort">Filter:</label>
|
|
||||||
<span>todo</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SortSelect />
|
<SortSelect />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default function SortSelect() {
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
{/* Toggle button to open the dropdown */}
|
{/* Toggle button to open the dropdown */}
|
||||||
<button type="button" {...getToggleButtonProps()} className="pill input w-full gap-1 !justify-between">
|
<button type="button" {...getToggleButtonProps()} className="pill input w-full gap-1 !justify-between text-nowrap">
|
||||||
<span>Sort by </span>
|
<span>Sort by </span>
|
||||||
{selectedItem || "Select a way to sort"}
|
{selectedItem || "Select a way to sort"}
|
||||||
<Icon icon="tabler:chevron-down" className="ml-2 size-5" />
|
<Icon icon="tabler:chevron-down" className="ml-2 size-5" />
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export default function ImageList({ files, setFiles }: Props) {
|
||||||
className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400"
|
className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col justify-center w-full min-w-0">
|
<div className="flex flex-col justify-center w-full min-w-0">
|
||||||
<span className="font-semibold text overflow-hidden text-ellipsis">{file.name}</span>
|
<span className="font-semibold overflow-hidden text-ellipsis">{file.name}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(index)}
|
onClick={() => handleDelete(index)}
|
||||||
className="pill button text-xs w-min !px-3 !py-1 !bg-red-300 !border-red-400 hover:!bg-red-400"
|
className="pill button text-xs w-min !px-3 !py-1 !bg-red-300 !border-red-400 hover:!bg-red-400"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { convertQrCode } from "@/lib/qr-codes";
|
||||||
import Mii from "@/lib/mii.js/mii";
|
import Mii from "@/lib/mii.js/mii";
|
||||||
import TomodachiLifeMii from "@/lib/tomodachi-life-mii";
|
import TomodachiLifeMii from "@/lib/tomodachi-life-mii";
|
||||||
|
|
||||||
import TagSelector from "./tag-selector";
|
import TagSelector from "../tag-selector";
|
||||||
import ImageList from "./image-list";
|
import ImageList from "./image-list";
|
||||||
import QrUpload from "./qr-upload";
|
import QrUpload from "./qr-upload";
|
||||||
import QrScanner from "./qr-scanner";
|
import QrScanner from "./qr-scanner";
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ interface Props {
|
||||||
setTags: React.Dispatch<React.SetStateAction<string[]>>;
|
setTags: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagRegex = /^[a-z]*$/;
|
const tagRegex = /^[a-z-]*$/;
|
||||||
const predefinedItems = ["anime", "art", "cartoon", "celebrity", "games", "history", "meme", "movie", "oc", "tv"];
|
const predefinedTags = ["anime", "art", "cartoon", "celebrity", "games", "history", "meme", "movie", "oc", "tv"];
|
||||||
|
|
||||||
export default function TagSelector({ tags, setTags }: Props) {
|
export default function TagSelector({ tags, setTags }: Props) {
|
||||||
const [inputValue, setInputValue] = useState<string>("");
|
const [inputValue, setInputValue] = useState<string>("");
|
||||||
|
|
||||||
const getFilteredItems = (): string[] =>
|
const getFilteredItems = (): string[] =>
|
||||||
predefinedItems.filter((item) => item.toLowerCase().includes(inputValue?.toLowerCase() || "")).filter((item) => !tags.includes(item));
|
predefinedTags.filter((item) => item.toLowerCase().includes(inputValue?.toLowerCase() || "")).filter((item) => !tags.includes(item));
|
||||||
|
|
||||||
const filteredItems = getFilteredItems();
|
const filteredItems = getFilteredItems();
|
||||||
const isMaxItemsSelected = tags.length >= 8;
|
const isMaxItemsSelected = tags.length >= 8;
|
||||||
|
|
@ -36,7 +36,7 @@ export default function TagSelector({ tags, setTags }: Props) {
|
||||||
inputValue,
|
inputValue,
|
||||||
items: filteredItems,
|
items: filteredItems,
|
||||||
onInputValueChange: ({ inputValue }) => {
|
onInputValueChange: ({ inputValue }) => {
|
||||||
if (!tagRegex.test(inputValue)) return;
|
if (inputValue && !tagRegex.test(inputValue)) return;
|
||||||
setInputValue(inputValue || "");
|
setInputValue(inputValue || "");
|
||||||
},
|
},
|
||||||
onStateChange: ({ type, selectedItem }) => {
|
onStateChange: ({ type, selectedItem }) => {
|
||||||
Loading…
Reference in a new issue