diff --git a/src/components/search/SearchBar.js b/src/components/search/SearchBar.js index 9844e3b..3c53a5b 100644 --- a/src/components/search/SearchBar.js +++ b/src/components/search/SearchBar.js @@ -1,146 +1,200 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { InputText } from 'primereact/inputtext'; -import { InputIcon } from 'primereact/inputicon'; -import { IconField } from 'primereact/iconfield'; -import { Dropdown } from 'primereact/dropdown'; -import { OverlayPanel } from 'primereact/overlaypanel'; -import ContentDropdownItem from '@/components/content/dropdowns/ContentDropdownItem'; -import MessageDropdownItem from '@/components/content/dropdowns/MessageDropdownItem'; -import { useContentSearch } from '@/hooks/useContentSearch'; -import { useCommunitySearch } from '@/hooks/useCommunitySearch'; -import { useRouter } from 'next/router'; -import useWindowWidth from '@/hooks/useWindowWidth'; -import styles from './searchbar.module.css'; +import React, { useState, useRef, useEffect } from "react"; +import { InputText } from "primereact/inputtext"; +import { InputIcon } from "primereact/inputicon"; +import { IconField } from "primereact/iconfield"; +import { Dropdown } from "primereact/dropdown"; +import { OverlayPanel } from "primereact/overlaypanel"; +import ContentDropdownItem from "@/components/content/dropdowns/ContentDropdownItem"; +import MessageDropdownItem from "@/components/content/dropdowns/MessageDropdownItem"; +import { useContentSearch } from "@/hooks/useContentSearch"; +import { useCommunitySearch } from "@/hooks/useCommunitySearch"; +import { useRouter } from "next/router"; +import useWindowWidth from "@/hooks/useWindowWidth"; +import styles from "./searchbar.module.css"; const SearchBar = () => { - const { searchContent, searchResults: contentResults } = useContentSearch(); - const { searchCommunity, searchResults: communityResults } = useCommunitySearch(); - const router = useRouter(); - const windowWidth = useWindowWidth(); - const [selectedSearchOption, setSelectedSearchOption] = useState({ name: 'Content', code: 'content', icon: 'pi pi-video' }); - const searchOptions = [ - { name: 'Content', code: 'content', icon: 'pi pi-video' }, - { name: 'Community', code: 'community', icon: 'pi pi-users' }, - ]; - const [searchTerm, setSearchTerm] = useState(''); - const [searchResults, setSearchResults] = useState([]); - const op = useRef(null); + const { searchContent, searchResults: contentResults } = useContentSearch(); + const { searchCommunity, searchResults: communityResults } = + useCommunitySearch(); + const router = useRouter(); + const windowWidth = useWindowWidth(); + const [selectedSearchOption, setSelectedSearchOption] = useState({ + name: "Content", + code: "content", + icon: "pi pi-video", + }); + const searchOptions = [ + { name: "Content", code: "content", icon: "pi pi-video" }, + { name: "Community", code: "community", icon: "pi pi-users" }, + ]; + const [searchTerm, setSearchTerm] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const op = useRef(null); + const [isSearchActive, setIsSearchActive] = useState(false); - const selectedOptionTemplate = (option, props) => { - if (!props?.placeholder) { - return ( -
- - {option.code} -
- ); - } - return - }; + const selectedOptionTemplate = (option, props) => { + if (!props?.placeholder) { + return ( +
+ + {option.code} +
+ ); + } + return ; + }; - const handleSearch = (e) => { - const term = e.target.value; - setSearchTerm(term); - - if (selectedSearchOption.code === 'content') { - searchContent(term); - setSearchResults(contentResults); - } else if (selectedSearchOption.code === 'community') { - searchCommunity(term); - setSearchResults(communityResults); - } + const handleSearch = (e) => { + const term = e.target.value; + setSearchTerm(term); - if (term.length > 2) { - op.current.show(e); - } else { - op.current.hide(); - } - }; - - useEffect(() => { - if (selectedSearchOption.code === 'content') { - setSearchResults(contentResults); - } else if (selectedSearchOption.code === 'community') { - setSearchResults(communityResults); - } - }, [selectedSearchOption, contentResults, communityResults]); - - const handleContentSelect = (content) => { - if (content?.type === 'course') { - router.push(`/course/${content.id}`); - } else { - router.push(`/details/${content.id}`); - } - setSearchTerm(''); - searchContent(''); - op.current.hide(); + if (selectedSearchOption.code === "content") { + searchContent(term); + setSearchResults(contentResults); + } else if (selectedSearchOption.code === "community") { + searchCommunity(term); + setSearchResults(communityResults); } - return ( -
- - - 845 ? 'w-[300px]' : 'w-[160px]'}`} - value={searchTerm} - onChange={handleSearch} - placeholder={`Search ${selectedSearchOption.name.toLowerCase()}`} - pt={{ - root: { - className: 'border-none rounded-tr-none rounded-br-none focus:border-none focus:ring-0 pr-0' - } - }} - /> + if (term.length > 2) { + setIsSearchActive(true); + op.current.show(e); + } else { + setIsSearchActive(false); + op.current.hide(); + } + }; - setSelectedSearchOption(e.value)} - options={searchOptions} - optionLabel="name" - placeholder="Search" - dropdownIcon={ -
- - -
- } - valueTemplate={selectedOptionTemplate} - itemTemplate={selectedOptionTemplate} - required - /> -
+ useEffect(() => { + if (selectedSearchOption.code === "content") { + setSearchResults(contentResults); + } else if (selectedSearchOption.code === "community") { + setSearchResults(communityResults); + } + }, [selectedSearchOption, contentResults, communityResults]); - - {searchResults.map((item, index) => ( - item.type === 'discord' || item.type === 'nostr' || item.type === 'stackernews' ? ( - - ) : ( - - ) - ))} - {searchResults.length === 0 && searchTerm.length > 2 && ( -
No results found
- )} -
-
- ); + useEffect(() => { + const mainContent = document.querySelector(".main-content"); + if (mainContent) { + if (isSearchActive) { + mainContent.classList.add(styles.blurredContent); + } else { + mainContent.classList.remove(styles.blurredContent); + } + } + }, [isSearchActive]); + + useEffect(() => { + const handleClickOutside = (event) => { + const overlayElement = document.querySelector(".p-overlaypanel"); + const searchElement = event.target.closest(".search-container"); + + if ( + (!overlayElement || !overlayElement.contains(event.target)) && + !searchElement + ) { + setSearchTerm(""); + setIsSearchActive(false); + setSearchResults([]); + op.current?.hide(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + const handleContentSelect = (content) => { + if (content?.type === "course") { + router.push(`/course/${content.id}`); + } else { + router.push(`/details/${content.id}`); + } + setSearchTerm(""); + searchContent(""); + setIsSearchActive(false); + op.current.hide(); + }; + + return ( +
+ + + 845 ? "w-[300px]" : "w-[160px]"}`} + value={searchTerm} + onChange={handleSearch} + placeholder={`Search ${selectedSearchOption.name.toLowerCase()}`} + pt={{ + root: { + className: + "border-none rounded-tr-none rounded-br-none focus:border-none focus:ring-0 pr-0", + }, + }} + /> + + setSelectedSearchOption(e.value)} + options={searchOptions} + optionLabel="name" + placeholder="Search" + dropdownIcon={ +
+ + +
+ } + valueTemplate={selectedOptionTemplate} + itemTemplate={selectedOptionTemplate} + required + /> +
+ + + {searchResults.map((item, index) => + item.type === "discord" || + item.type === "nostr" || + item.type === "stackernews" ? ( + + ) : ( + + ) + )} + {searchResults.length === 0 && searchTerm.length > 2 && ( +
No results found
+ )} +
+
+ ); }; export default SearchBar; diff --git a/src/components/search/searchbar.module.css b/src/components/search/searchbar.module.css index f9dd596..4c4acbc 100644 --- a/src/components/search/searchbar.module.css +++ b/src/components/search/searchbar.module.css @@ -17,3 +17,23 @@ outline: none !important; box-shadow: none !important; } */ + +.dropdown { + background: rgba(0, 0, 0, 0.3) !important; + backdrop-filter: blur(8px) !important; +} + +/* Add this to style the overlay panel */ +.overlayPanel { + background: rgba(0, 0, 0, 0.75) !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; +} + +.overlayPanelContent { + background: transparent !important; +} + +.blurredContent { + filter: blur(8px); + transition: filter 0.2s ease-in-out; +} diff --git a/src/pages/_app.js b/src/pages/_app.js index 496dc50..87b3d95 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -1,61 +1,61 @@ -import { PrimeReactProvider } from 'primereact/api'; -import { useEffect, useState } from 'react'; -import Navbar from '@/components/navbar/Navbar'; -import { ToastProvider } from '@/hooks/useToast'; -import { SessionProvider } from "next-auth/react" -import Layout from '@/components/Layout'; -import '@/styles/globals.css' -import 'primereact/resources/themes/lara-dark-blue/theme.css' -import '@/styles/custom-theme.css'; // custom theme -import 'primereact/resources/primereact.min.css'; -import 'primeicons/primeicons.css'; +import { PrimeReactProvider } from "primereact/api"; +import { useEffect, useState } from "react"; +import Navbar from "@/components/navbar/Navbar"; +import { ToastProvider } from "@/hooks/useToast"; +import { SessionProvider } from "next-auth/react"; +import Layout from "@/components/Layout"; +import "@/styles/globals.css"; +import "primereact/resources/themes/lara-dark-blue/theme.css"; +import "@/styles/custom-theme.css"; // custom theme +import "primereact/resources/primereact.min.css"; +import "primeicons/primeicons.css"; import "@uiw/react-md-editor/markdown-editor.css"; import "@uiw/react-markdown-preview/markdown.css"; -import Sidebar from '@/components/sidebar/Sidebar'; -import { useRouter } from 'next/router'; -import { NDKProvider } from '@/context/NDKContext'; -import { Analytics } from '@vercel/analytics/react'; -import { - QueryClient, - QueryClientProvider, -} from '@tanstack/react-query' -import BottomBar from '@/components/BottomBar'; +import Sidebar from "@/components/sidebar/Sidebar"; +import { useRouter } from "next/router"; +import { NDKProvider } from "@/context/NDKContext"; +import { Analytics } from "@vercel/analytics/react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import BottomBar from "@/components/BottomBar"; -const queryClient = new QueryClient() +const queryClient = new QueryClient(); export default function MyApp({ - Component, pageProps: { session, ...pageProps } + Component, + pageProps: { session, ...pageProps }, }) { - const [isCourseView, setIsCourseView] = useState(false); - const router = useRouter(); + const [isCourseView, setIsCourseView] = useState(false); + const router = useRouter(); - useEffect(() => { - setIsCourseView(router.pathname.includes('course') && !router.pathname.includes('draft')); - }, [router.pathname]); - - return ( - - - - - - -
- -
- -
- - -
-
- -
-
-
-
-
-
-
+ useEffect(() => { + setIsCourseView( + router.pathname.includes("course") && !router.pathname.includes("draft") ); -} \ No newline at end of file + }, [router.pathname]); + + return ( + + + + + + +
+ +
+ +
+ + +
+
+ +
+
+
+
+
+
+
+ ); +}