Skip to content

Commit

Permalink
feat: update
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuba-Ahhh committed Aug 23, 2024
1 parent a8f2aff commit 1ec946c
Show file tree
Hide file tree
Showing 9 changed files with 928 additions and 14 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"clean": "rm -rf .next"
},
"dependencies": {
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
601 changes: 601 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

63 changes: 53 additions & 10 deletions src/app/blog/BlogClientComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@ import {
Alert,
AlertDescription,
AlertTitle,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui";
import { ChevronDown, ChevronUp } from "lucide-react";
import { AlertCircle } from "lucide-react";

const POSTS_PER_PAGE = 6;
const INITIAL_TAG_COUNT = 10;

// 添加新的排序选项类型
type SortOption = "date" | "title";

export default function BlogList() {
const router = useRouter();
const searchParams = useSearchParams();
Expand All @@ -33,6 +42,7 @@ export default function BlogList() {
const [filteredPosts, setFilteredPosts] = useState(blogPosts);
const [isTagsExpanded, setIsTagsExpanded] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [sortOption, setSortOption] = useState<SortOption>("date");

const handleSearchParamsChange = useCallback(() => {
const tag = searchParams.get("tag");
Expand All @@ -54,13 +64,24 @@ export default function BlogList() {
}, []);

const filteredPostsMemo = useMemo(() => {
return filteredPosts.filter(
let posts = filteredPosts.filter(
(post) =>
(post.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
post.content.toLowerCase().includes(searchTerm.toLowerCase())) &&
(!selectedTag || post.tags.includes(selectedTag))
);
}, [searchTerm, selectedTag, filteredPosts]);

// 根据选择的排序选项进行排序
posts.sort((a, b) => {
if (sortOption === "date") {
return new Date(b.date).getTime() - new Date(a.date).getTime();
} else {
return a.title.localeCompare(b.title);
}
});

return posts;
}, [searchTerm, selectedTag, filteredPosts, sortOption]);

const currentPosts = useMemo(() => {
const indexOfLastPost = currentPage * POSTS_PER_PAGE;
Expand Down Expand Up @@ -111,14 +132,36 @@ export default function BlogList() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-8 text-center">博客文章</h1>
<div className="mb-6 relative">
<Input
type="text"
placeholder="搜索文章..."
value={searchTerm}
onChange={handleSearch}
className="w-full"
/>
<div className="mb-6 flex items-end space-x-4">
<div className="flex-grow">
<Label htmlFor="search" className="mb-2 block">
搜索文章
</Label>
<Input
id="search"
type="text"
placeholder="输入关键词..."
value={searchTerm}
onChange={handleSearch}
/>
</div>
<div className="w-40">
<Label htmlFor="sort" className="mb-2 block">
排序方式
</Label>
<Select
onValueChange={(value: SortOption) => setSortOption(value)}
defaultValue={sortOption}
>
<SelectTrigger id="sort" className="w-full">
<SelectValue placeholder="选择排序方式" />
</SelectTrigger>
<SelectContent>
<SelectItem value="date">按日期排序</SelectItem>
<SelectItem value="title">按标题排序</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="mb-6 flex flex-wrap items-center gap-2">
<Badge
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ export default function Footer() {
</div>
</footer>
);
}
}
7 changes: 7 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './ui';
export * from './Comments';
export * from './Footer';
export * from './Header';
export * from './Layout';
export * from './ReadingProgress';
export * from './ThemeToggle'
2 changes: 2 additions & 0 deletions src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from "./skeleton";
export * from "./textarea";
export * from "./card";
export * from "./alert";
export * from "./select";
export * from "./label";
26 changes: 26 additions & 0 deletions src/components/ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client"

import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"

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

const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName

export { Label }
160 changes: 160 additions & 0 deletions src/components/ui/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"use client";

import * as React from "react";
import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown, ChevronUp } from "lucide-react";

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

const Select = SelectPrimitive.Root;

const SelectGroup = SelectPrimitive.Group;

const SelectValue = SelectPrimitive.Value;

const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;

const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;

const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;

const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;

const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;

const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>

<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;

const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;

export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
};
Loading

0 comments on commit 1ec946c

Please sign in to comment.