Skip to content

Commit

Permalink
feat: pagination for Radar Rules (#889)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyoban authored Feb 11, 2024
1 parent abfbd7f commit 81d0581
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 50 deletions.
6 changes: 6 additions & 0 deletions assets/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,11 @@
},
"current": {
"message": "Current"
},
"paginationPrevious": {
"message": "Previous"
},
"paginationNext": {
"message": "Next"
}
}
6 changes: 6 additions & 0 deletions assets/locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,11 @@
},
"current": {
"message": "当前"
},
"paginationPrevious": {
"message": "上一页"
},
"paginationNext": {
"message": "下一页"
}
}
102 changes: 102 additions & 0 deletions src/lib/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import * as React from "react"

import { Button, type ButtonProps } from "~/lib/components/Button"
import { cn } from "~/lib/utils"

const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
)
Pagination.displayName = "Pagination"

const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
))
PaginationContent.displayName = "PaginationContent"

const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
))
PaginationItem.displayName = "PaginationItem"

type PaginationLinkProps = {
isActive?: boolean
} & Omit<ButtonProps, "variant">

const PaginationLink = ({ isActive, ...props }: PaginationLinkProps) => (
<Button
aria-current={isActive ? "page" : undefined}
variant={isActive ? "outline" : "ghost"}
{...props}
/>
)
PaginationLink.displayName = "PaginationLink"

const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>{chrome.i18n.getMessage("paginationPrevious")}</span>
</PaginationLink>
)
PaginationPrevious.displayName = "PaginationPrevious"

const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>{chrome.i18n.getMessage("paginationNext")}</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
)
PaginationNext.displayName = "PaginationNext"

const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
</span>
)
PaginationEllipsis.displayName = "PaginationEllipsis"

export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
}
192 changes: 142 additions & 50 deletions src/options/routes/Rules.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from "lodash"
import { useEffect, useState } from "react"
import { useEffect, useMemo, useState } from "react"

import { sendToBackground } from "@plasmohq/messaging"

Expand All @@ -9,13 +9,139 @@ import {
AccordionItem,
AccordionTrigger,
} from "~/lib/components/Accordion"
import { Card, CardContent } from "~/lib/components/Card"
import { Card, CardContent, CardFooter } from "~/lib/components/Card"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "~/lib/components/Pagination"
import report from "~/lib/report"
import { getRulesCount, parseRules } from "~/lib/rules"
import type { Rules as IRules } from "~/lib/types"

function RulesPagination({
currentPage,
setCurrentPage,
pageSize,
itemsCount,
}: {
currentPage: number
setCurrentPage: (page: number) => void
pageSize: number
itemsCount: number
}) {
const totalPage = Math.ceil(itemsCount / pageSize)
return (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
disabled={currentPage <= 1}
onClick={() => setCurrentPage(currentPage - 1)}
/>
</PaginationItem>
{Array.from({ length: totalPage }).map((_, index) => {
const page = index + 1
if (
page === 1 ||
page === totalPage ||
(page >= currentPage - 1 && page <= currentPage + 1)
) {
return (
<PaginationItem key={page}>
<PaginationLink
isActive={page === currentPage}
onClick={() => setCurrentPage(page)}
>
{page}
</PaginationLink>
</PaginationItem>
)
} else if (page === currentPage - 2 || page === currentPage + 2) {
return (
<PaginationItem key={page}>
<PaginationEllipsis />
</PaginationItem>
)
}
})}
<PaginationItem>
<PaginationNext
disabled={currentPage >= totalPage}
onClick={() => setCurrentPage(currentPage + 1)}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}

function RulesList({
rules,
start,
end,
}: {
rules: IRules
start: number
end: number
}) {
const rulesKeys = useMemo(() => Object.keys(rules), [rules])

return rulesKeys.slice(start, end).map((key) => {
const rule = rules[key]
return (
<div key={key}>
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>
<div className="font-medium">
{rule._name} - {key}
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
{Object.keys(rule).map((item) => {
const subdomainRules = rule[item]
if (Array.isArray(subdomainRules)) {
return subdomainRules.map((subdomainRule, index) => {
return (
<div key={key + item + index}>
<div className="flex items-center space-x-2">
<div className="flex-1">
<p className="text-sm text-zinc-700">
{subdomainRule.title}
</p>
</div>
</div>
</div>
)
})
} else {
return null
}
})}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
})
}

const pageSize = 20

function Rules() {
const [rules, setRules] = useState<IRules>({})
const rulesCount = useMemo(() => getRulesCount(rules), [rules])

const [currentPage, setCurrentPage] = useState(1)
const itemsCount = useMemo(() => Object.keys(rules).length, [rules])

useEffect(() => {
sendToBackground({
name: "requestDisplayedRules",
Expand All @@ -25,19 +151,14 @@ function Rules() {
})
}, [])

const [count, setCount] = useState(0)
useEffect(() => {
setCount(getRulesCount(rules))
}, [rules])

return (
<div>
<h1 className="text-3xl font-medium leading-10 mb-6 text-orange-500 border-b pb-4">
{chrome.i18n.getMessage("rules")}
</h1>
<div className="content mb-6 space-y-2">
<p>
{chrome.i18n.getMessage("totalNumberOfRules")}: {count}
{chrome.i18n.getMessage("totalNumberOfRules")}: {rulesCount}
</p>
<p
dangerouslySetInnerHTML={{
Expand All @@ -48,49 +169,20 @@ function Rules() {
<div className="space-y-4">
<Card>
<CardContent>
{Object.keys(rules).map((key) => {
const rule = rules[key]
return (
<div key={key}>
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>
<div className="font-medium">
{rule._name} - {key}
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
{Object.keys(rule).map((item) => {
const subdomainRules = rule[item]
if (Array.isArray(subdomainRules)) {
return subdomainRules.map(
(subdomainRule, index) => {
return (
<div key={key + item + index}>
<div className="flex items-center space-x-2">
<div className="flex-1">
<p className="text-sm text-zinc-700">
{subdomainRule.title}
</p>
</div>
</div>
</div>
)
},
)
} else {
return null
}
})}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
})}
<RulesList
rules={rules}
start={(currentPage - 1) * pageSize}
end={currentPage * pageSize}
/>
</CardContent>
<CardFooter>
<RulesPagination
currentPage={currentPage}
setCurrentPage={setCurrentPage}
pageSize={pageSize}
itemsCount={itemsCount}
/>
</CardFooter>
</Card>
</div>
</div>
Expand Down

0 comments on commit 81d0581

Please sign in to comment.