diff --git a/src/app/globals.css b/src/app/globals.css index 607cc9a..b49b1bd 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,7 +32,7 @@ body { } .pill { - @apply flex justify-center items-center px-5 py-2 bg-orange-300 border-2 border-orange-400 rounded-4xl shadow-md; + @apply flex justify-center items-center px-5 py-2 bg-orange-300 border-2 border-orange-400 rounded-3xl shadow-md; } .button { diff --git a/src/components/submit-form/edit-form.tsx b/src/components/submit-form/edit-form.tsx index 4d44c70..46d7757 100644 --- a/src/components/submit-form/edit-form.tsx +++ b/src/components/submit-form/edit-form.tsx @@ -164,7 +164,7 @@ export default function EditForm({ mii, likes }: Props) { - +
diff --git a/src/components/submit-form/index.tsx b/src/components/submit-form/index.tsx index bdb0614..becd708 100644 --- a/src/components/submit-form/index.tsx +++ b/src/components/submit-form/index.tsx @@ -182,7 +182,7 @@ export default function SubmitForm() { - +
diff --git a/src/components/tag-selector.tsx b/src/components/tag-selector.tsx index 0eb393d..1bf6931 100644 --- a/src/components/tag-selector.tsx +++ b/src/components/tag-selector.tsx @@ -1,19 +1,21 @@ "use client"; -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import { useCombobox } from "downshift"; import { Icon } from "@iconify/react"; interface Props { tags: string[]; setTags: React.Dispatch>; + showTagLimit?: boolean; } const tagRegex = /^[a-z0-9-_]*$/; 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, showTagLimit }: Props) { const [inputValue, setInputValue] = useState(""); + const inputRef = useRef(null); const getFilteredItems = (): string[] => predefinedTags.filter((item) => item.toLowerCase().includes(inputValue?.toLowerCase() || "")).filter((item) => !tags.includes(item)); @@ -23,7 +25,7 @@ export default function TagSelector({ tags, setTags }: Props) { const hasSelectedItems = tags.length > 0; const addTag = (tag: string) => { - if (!tags.includes(tag) && tags.length < 8) { + if (!tags.includes(tag) && tags.length < 8 && tag.length <= 20) { setTags([...tags, tag]); } }; @@ -32,7 +34,7 @@ export default function TagSelector({ tags, setTags }: Props) { setTags(tags.filter((t) => t !== tag)); }; - const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, getItemProps, highlightedIndex } = useCombobox({ + const { isOpen, openMenu, getToggleButtonProps, getMenuProps, getInputProps, getItemProps, highlightedIndex } = useCombobox({ inputValue, items: filteredItems, onInputValueChange: ({ inputValue }) => { @@ -61,65 +63,82 @@ export default function TagSelector({ tags, setTags }: Props) { } }; + const handleContainerClick = () => { + if (!isMaxItemsSelected) { + inputRef.current?.focus(); + openMenu(); + } + }; + return ( -
0 ? "py-1.5!" : "" - }`} - > - {/* Tags */} -
- {tags.map((tag) => ( - - {tag} - + + ))} + + {/* Input */} + 0 ? "" : "Type or select a tag...", + maxLength: 20, + className: "w-full flex-1 outline-none placeholder:text-black/40", + })} + /> +
+ + {/* Control buttons */} +
e.stopPropagation()}> + {hasSelectedItems && ( + - - ))} + )} - {/* Input */} - 0 ? "" : "Type or select a tag...", - className: "w-full flex-1 outline-none placeholder:text-black/40", - })} - /> -
- - {/* Control buttons */} -
- {hasSelectedItems && ( - - )} +
- -
- - {/* Dropdown menu */} - {!isMaxItemsSelected && ( -
    - {isOpen && - filteredItems.map((item, index) => ( + {/* Dropdown menu */} + {!isMaxItemsSelected && ( +
      e.stopPropagation()} + className={`absolute right-0 top-full mt-2 z-50 w-80 bg-orange-200/45 backdrop-blur-md border-2 border-orange-400 rounded-lg shadow-lg shadow-black/25 max-h-60 overflow-y-auto ${ + isOpen ? "block" : "hidden" + }`} + > + {filteredItems.map((item, index) => (
    • ))} - {isOpen && inputValue && !filteredItems.includes(inputValue) && ( -
    • { - addTag(inputValue); - setInputValue(""); - }} - > - Add "{inputValue}" -
    • + {inputValue && !filteredItems.includes(inputValue) && ( +
    • { + addTag(inputValue); + setInputValue(""); + }} + > + Add "{inputValue}" +
    • + )} +
    + )} +
+ + {/* Tag limit message */} + {showTagLimit && ( +
+ {isMaxItemsSelected ? ( + Maximum of 8 tags reached. Remove a tag to add more. + ) : ( + {tags.length}/8 tags )} - +
)} ); diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index 21a5d09..102ac10 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -26,7 +26,7 @@ export const tagsSchema = z z .string() .min(2, { error: "Tags must be at least 2 characters long" }) - .max(64, { error: "Tags cannot be more than 20 characters long" }) + .max(20, { error: "Tags cannot be more than 20 characters long" }) .regex(/^[a-z0-9-_]+$/, { error: "Tags can only contain lowercase letters, numbers, dashes, and underscores.", })