From 9b12482921224b22ed806fb68f024290f56c709d Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Fri, 10 Apr 2026 00:32:12 -0400 Subject: [PATCH] Replace native select elements with custom themed SelectDropdown - New SelectDropdown component with themed styling matching item/talent pickers - Replaced all 6 native setCustomType(e.target.value)} - > - - - - - + setCustomType(v.toLowerCase())} + /> diff --git a/client/src/components/InfoPanel.tsx b/client/src/components/InfoPanel.tsx index 8579e78..a91624e 100644 --- a/client/src/components/InfoPanel.tsx +++ b/client/src/components/InfoPanel.tsx @@ -1,6 +1,7 @@ import { useRef, useEffect } from "react"; import type { Character } from "../types"; import TalentList from "./TalentList"; +import SelectDropdown from "./SelectDropdown"; import styles from "./InfoPanel.module.css"; const CLASSES = ["Fighter", "Priest", "Thief", "Wizard"]; @@ -100,31 +101,19 @@ export default function InfoPanel({
- + options={CLASSES} + onChange={(v) => handleField("class", v)} + />
- + options={ANCESTRIES} + onChange={(v) => handleField("ancestry", v)} + />
@@ -140,17 +129,11 @@ export default function InfoPanel({
- + options={ALIGNMENTS} + onChange={(v) => handleField("alignment", v)} + />
diff --git a/client/src/components/SelectDropdown.module.css b/client/src/components/SelectDropdown.module.css new file mode 100644 index 0000000..99c6d09 --- /dev/null +++ b/client/src/components/SelectDropdown.module.css @@ -0,0 +1,74 @@ +.container { + position: relative; + width: 100%; +} + +.trigger { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 0.3rem 0.5rem; + background: var(--bg-inset); + border: 1px solid rgba(var(--gold-rgb), 0.15); + border-radius: 4px; + color: var(--text-primary); + font-size: 0.85rem; + font-family: "Alegreya", Georgia, serif; + cursor: pointer; + text-align: left; +} + +.trigger:hover { + border-color: rgba(var(--gold-rgb), 0.35); +} + +.arrow { + font-size: 0.65rem; + color: var(--text-tertiary); + margin-left: 0.5rem; +} + +.dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 0.2rem; + background-color: var(--bg-modal); + background-image: var(--texture-surface); + background-size: 256px 256px; + background-repeat: repeat; + border: 1px solid rgba(var(--gold-rgb), 0.3); + border-radius: 4px; + z-index: 200; + max-height: 180px; + overflow-y: auto; + box-shadow: + 0 4px 16px rgba(var(--shadow-rgb), 0.5), + inset 0 1px 0 rgba(var(--gold-rgb), 0.06); + scrollbar-width: thin; + scrollbar-color: rgba(var(--gold-rgb), 0.15) transparent; +} + +.option { + display: block; + width: 100%; + padding: 0.35rem 0.6rem; + background: none; + border: none; + color: var(--text-primary); + font-size: 0.85rem; + font-family: "Alegreya", Georgia, serif; + cursor: pointer; + text-align: left; +} + +.option:hover { + background: rgba(var(--gold-rgb), 0.12); +} + +.option.active { + color: var(--gold); + font-weight: 600; +} diff --git a/client/src/components/SelectDropdown.tsx b/client/src/components/SelectDropdown.tsx new file mode 100644 index 0000000..535e44f --- /dev/null +++ b/client/src/components/SelectDropdown.tsx @@ -0,0 +1,63 @@ +import { useState, useRef, useEffect } from "react"; +import styles from "./SelectDropdown.module.css"; + +interface SelectDropdownProps { + value: string; + options: string[]; + onChange: (value: string) => void; + className?: string; +} + +export default function SelectDropdown({ + value, + options, + onChange, + className, +}: SelectDropdownProps) { + const [open, setOpen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!open) return; + function handleClickOutside(e: MouseEvent) { + if ( + containerRef.current && + !containerRef.current.contains(e.target as Node) + ) { + setOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [open]); + + return ( +
+ + {open && ( +
+ {options.map((opt) => ( + + ))} +
+ )} +
+ ); +} diff --git a/client/src/pages/CampaignView.tsx b/client/src/pages/CampaignView.tsx index 83cda54..ea455d8 100644 --- a/client/src/pages/CampaignView.tsx +++ b/client/src/pages/CampaignView.tsx @@ -19,6 +19,7 @@ import CharacterCard from "../components/CharacterCard"; import CharacterDetail from "../components/CharacterDetail"; import RollLog from "../components/RollLog"; import DiceTray from "../components/DiceTray"; +import SelectDropdown from "../components/SelectDropdown"; import styles from "./CampaignView.module.css"; const CLASSES = ["Fighter", "Priest", "Thief", "Wizard"]; @@ -400,35 +401,19 @@ export default function CampaignView() {
- + options={CLASSES} + onChange={(v) => setNewChar({ ...newChar, class: v })} + />
- + options={ANCESTRIES} + onChange={(v) => setNewChar({ ...newChar, ancestry: v })} + />