Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
24 changes: 24 additions & 0 deletions src/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react";

import { cn } from "@/lib/utils";

export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
);
}
);
Input.displayName = "Input";

export { Input };
26 changes: 26 additions & 0 deletions src/features/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { TiFilter } from "react-icons/ti";
import { TbTextDirectionRtl } from "react-icons/tb";
import { IoLogoGithub } from "react-icons/io";
import { BiMenu } from "react-icons/bi";
import { IoSearch } from "react-icons/io5";
import { IoClose } from "react-icons/io5";
import { ToggleButton } from "@/components/toggleButton";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { SettingMenu } from "../settingMenu";
import { useHeader } from "./viewModel";

Expand All @@ -18,6 +21,7 @@ export function Header() {
marqueeTitleState,
displayHistoryState,
filterState,
titleFilter,
isDesktop,
} = useHeader();
const cn = isScrolled ? "shadow-lg border-b" : "shadow-none";
Expand All @@ -36,6 +40,28 @@ export function Header() {
Vspo stream schedule
</div>
<div className="ml-auto flex gap-2">
<div className="relative hidden md:flex items-center">
<IoSearch className="absolute left-3 size-4 text-muted-foreground pointer-events-none" />
<Input
type="text"
placeholder="Filter by title..."
value={titleFilter.value}
onChange={(e) => titleFilter.onChange(e.target.value)}
className="pl-9 pr-9 w-64"
/>
{titleFilter.value && (
<Button
variant="ghost"
size="icon"
onClick={() => titleFilter.onChange("")}
className="absolute right-0 h-9 w-9 hover:bg-transparent"
>
<IoClose className="size-4 text-muted-foreground hover:text-foreground" />
</Button>
)}
</div>
</div>
<div className="flex gap-2">
{isDesktop && (
<div>
<ToggleButton {...themeState} className="rounded-none rounded-l-md">
Expand Down
13 changes: 12 additions & 1 deletion src/features/header/viewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useMediaQuery } from "react-responsive";

export function useHeader() {
const [isScrolled, setIsScrolled] = useState(false);
const { theme, isMarqueeTitle, isDisplayHistory, filteredStreamerIds } =
const { theme, isMarqueeTitle, isDisplayHistory, filteredStreamerIds, filteredTitle } =
useSettings();
const dispatch = useSettingDispatch();
const isDesktop = useMediaQuery({ query: "(min-width: 768px)" });
Expand Down Expand Up @@ -64,13 +64,24 @@ export function useHeader() {
description: "Filter by streamer",
};

const titleFilter = {
value: filteredTitle,
onChange: (value: string) => {
dispatch({
target: "filteredTitle",
payload: value,
});
},
};

return {
isScrolled,
onClickGithubIcon,
themeState,
marqueeTitleState,
displayHistoryState,
filterState,
titleFilter,
isDesktop,
};
}