Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ export function Dropdown({
children,
open,
onOpenChange,
modal = false,
modal = false, // IMPORTANT DEFAULT
}: DropdownProps) {
return (
<DropdownMenu.Root open={open} onOpenChange={onOpenChange} modal={modal}>
<DropdownMenu.Root
open={open}
onOpenChange={onOpenChange}
modal={modal}
>
{children}
</DropdownMenu.Root>
)
Expand Down Expand Up @@ -69,6 +73,16 @@ export function DropdownContent({
<DropdownMenu.Content
align={align}
sideOffset={sideOffset}
onCloseAutoFocus={(e) => e.preventDefault()}
onInteractOutside={(e) => {
// Only close if clicking far outside dropdown
const target = e.target as HTMLElement
if (!target.closest('[role="listbox"]') &&
!target.closest('button[class*="dropdown"]')) {
return // Allow the close
}
e.preventDefault() // Block close if clicking dropdown area
}}
className={twMerge(
'dropdown-content z-[1000] min-w-48 rounded-lg p-1.5',
'border border-gray-200 dark:border-gray-700',
Expand All @@ -83,6 +97,7 @@ export function DropdownContent({
)
}


export function DropdownItem({
children,
className,
Expand Down
88 changes: 61 additions & 27 deletions src/components/SearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ const searchClient = liteClient(
'10c34d6a5c89f6048cf644d601e65172',
)

// Context to share filter state between components
const SearchFiltersContext = React.createContext<{
selectedLibrary: string
selectedFramework: string
Expand All @@ -166,16 +165,20 @@ const SearchFiltersContext = React.createContext<{
value: string
label: string
count: number
isRefined: boolean
}>
frameworkItems: Array<{
value: string
label: string
count: number
isRefined: boolean
}>

libraryDropdownOpen: boolean
setLibraryDropdownOpen: (open: boolean) => void
frameworkDropdownOpen: boolean
setFrameworkDropdownOpen: (open: boolean) => void
} | null>(null)


function useSearchFilters() {
const context = React.useContext(SearchFiltersContext)
if (!context) {
Expand All @@ -190,7 +193,10 @@ function SearchFiltersProvider({ children }: { children: React.ReactNode }) {
const userQuery = useCurrentUserQuery()
const [selectedLibrary, setSelectedLibrary] = React.useState('')
const lastUsedFramework = userQuery.data?.lastUsedFramework

const [libraryDropdownOpen, setLibraryDropdownOpen] =
React.useState(false)
const [frameworkDropdownOpen, setFrameworkDropdownOpen] =
React.useState(false)
// Get initial framework from user preference (DB if logged in, localStorage otherwise)
const getInitialFramework = React.useCallback(() => {
if (lastUsedFramework) {
Expand Down Expand Up @@ -290,8 +296,13 @@ function SearchFiltersProvider({ children }: { children: React.ReactNode }) {
refineFramework: selectFramework,
libraryItems,
frameworkItems,
libraryDropdownOpen,
setLibraryDropdownOpen,
frameworkDropdownOpen,
setFrameworkDropdownOpen,
}}
>

{children}
</SearchFiltersContext.Provider>
)
Expand Down Expand Up @@ -556,16 +567,24 @@ const Hit = ({
}

function LibraryRefinement() {
const {
selectedLibrary,
setSelectedLibrary,
libraryItems: items,
} = useSearchFilters()
const {
selectedLibrary,
setSelectedLibrary,
libraryItems: items,
libraryDropdownOpen,
setLibraryDropdownOpen,
} = useSearchFilters()


const currentLibrary = libraries.find((l) => l.id === selectedLibrary)


return (
<Dropdown>
<Dropdown
open={libraryDropdownOpen}
onOpenChange={setLibraryDropdownOpen}
>

<DropdownTrigger asChild={false}>
<button className="flex items-center gap-1 text-sm focus:outline-none cursor-pointer font-bold">
{currentLibrary ? (
Expand All @@ -585,20 +604,27 @@ function LibraryRefinement() {
align="start"
className="max-h-[60vh] w-64 overflow-auto"
>
<DropdownItem
onSelect={() => setSelectedLibrary('')}
className="font-bold"
>
All Libraries
</DropdownItem>
<DropdownItem
onSelect={() => {
setSelectedLibrary('')
setLibraryDropdownOpen(false)
}}
className="font-bold"
>
All Libraries
</DropdownItem>

{items.map((item) => {
const lib = libraries.find((l) => l.id === item.value)
return (
<DropdownItem
key={item.value}
onSelect={() => setSelectedLibrary(item.value)}
className="justify-between"
>
key={item.value}
onSelect={() => {
setSelectedLibrary(item.value)
setLibraryDropdownOpen(false)
}}
className="justify-between"
>
<span className="uppercase font-black [letter-spacing:-.05em]">
<span className="opacity-50">TanStack</span>{' '}
<span className={lib?.textStyle ?? ''}>
Expand All @@ -617,16 +643,19 @@ function LibraryRefinement() {
}

function FrameworkRefinement() {
const {
selectedFramework,
setSelectedFramework,
frameworkItems: items,
} = useSearchFilters()
const {
selectedFramework,
setSelectedFramework,
frameworkItems: items,
frameworkDropdownOpen,
setFrameworkDropdownOpen,
} = useSearchFilters()

const persistFramework = usePersistFrameworkPreference()

const handleSelect = (value: string) => {
setSelectedFramework(value)
setFrameworkDropdownOpen(false)
if (value) {
persistFramework(value)
}
Expand All @@ -637,7 +666,12 @@ function FrameworkRefinement() {
)

return (
<Dropdown>
<Dropdown
open={frameworkDropdownOpen}
onOpenChange={setFrameworkDropdownOpen}
modal={true}
>

<DropdownTrigger asChild={false}>
<button className="flex items-center gap-1 text-sm font-bold focus:outline-none cursor-pointer">
{currentFramework && (
Expand All @@ -659,7 +693,7 @@ function FrameworkRefinement() {
align="start"
className="max-h-[60vh] w-52 overflow-auto"
>
<DropdownItem onSelect={() => handleSelect('')} className="font-bold">
<DropdownItem onSelect={() => {handleSelect('')}} className="font-bold">
All Frameworks
</DropdownItem>
{items.map((item) => {
Expand Down