@allbluecn/admin-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +86 -0
- package/src/client.tsx +31 -0
- package/src/components/admin/admin-user-menu.tsx +90 -0
- package/src/components/admin/developer-log.tsx +356 -0
- package/src/components/ai-provider/model-list-editor.tsx +147 -0
- package/src/components/ai-provider/provider-form.tsx +434 -0
- package/src/components/ai-provider/provider-icon.tsx +43 -0
- package/src/components/ai-provider/provider-logo.tsx +87 -0
- package/src/components/ai-provider/provider-table.tsx +258 -0
- package/src/components/auth/admin-login-form.tsx +214 -0
- package/src/components/auth/auth-brand-panel.tsx +60 -0
- package/src/components/auth/auth-header.tsx +38 -0
- package/src/components/auth/auth-layout.tsx +66 -0
- package/src/components/auth/auth-providers.tsx +34 -0
- package/src/components/auth/icon-input.tsx +46 -0
- package/src/components/auth/oauth-section.tsx +51 -0
- package/src/components/auth/password-input.tsx +67 -0
- package/src/components/common/page-header.tsx +42 -0
- package/src/components/common/placeholder-page.tsx +42 -0
- package/src/components/component/component-form-dialog.tsx +348 -0
- package/src/components/component/component-import-dialog.tsx +235 -0
- package/src/components/component/component-table.tsx +153 -0
- package/src/components/data-table/data-table-pagination.tsx +114 -0
- package/src/components/data-table/data-table.tsx +218 -0
- package/src/components/data-table/table-detail-layout.tsx +99 -0
- package/src/components/dependency/changelog-dialog.tsx +108 -0
- package/src/components/dependency/columns.tsx +232 -0
- package/src/components/dependency/dependency-table.tsx +368 -0
- package/src/components/dependency/edit-dialog.tsx +200 -0
- package/src/components/knowledge-public/bookmark-form-dialog.tsx +215 -0
- package/src/components/knowledge-public/compliance-form-dialog.tsx +308 -0
- package/src/components/knowledge-public/legal-form-dialog.tsx +315 -0
- package/src/components/layout/sidebar-02/app-sidebar.tsx +160 -0
- package/src/components/layout/sidebar-02/index.tsx +33 -0
- package/src/components/layout/sidebar-02/logo.tsx +40 -0
- package/src/components/layout/sidebar-02/nav-main.tsx +153 -0
- package/src/components/layout/sidebar-02/nav-notifications.tsx +78 -0
- package/src/components/layout/sidebar-02/team-switcher.tsx +106 -0
- package/src/components/magicui/animated-theme-toggler.tsx +272 -0
- package/src/components/magicui/rainbow-button.tsx +78 -0
- package/src/components/market/columns.tsx +151 -0
- package/src/components/market/market-app-form.tsx +237 -0
- package/src/components/market/market-app-list.tsx +156 -0
- package/src/components/monitoring/error-list.tsx +143 -0
- package/src/components/monitoring/health-cards.tsx +152 -0
- package/src/components/monitoring/metrics-stats.tsx +184 -0
- package/src/components/monitoring/request-log-table.tsx +179 -0
- package/src/components/providers/theme-toggle.tsx +59 -0
- package/src/components/settings/avatar/avatar-config.ts +46 -0
- package/src/components/ui/alert-dialog.tsx +216 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/avatar.tsx +129 -0
- package/src/components/ui/badge.tsx +66 -0
- package/src/components/ui/button.tsx +84 -0
- package/src/components/ui/card.tsx +120 -0
- package/src/components/ui/checkbox.tsx +50 -0
- package/src/components/ui/collapsible.tsx +50 -0
- package/src/components/ui/dialog.tsx +185 -0
- package/src/components/ui/dropdown-menu.tsx +286 -0
- package/src/components/ui/field.tsx +255 -0
- package/src/components/ui/input-group.tsx +171 -0
- package/src/components/ui/input.tsx +36 -0
- package/src/components/ui/label.tsx +41 -0
- package/src/components/ui/pagination.tsx +146 -0
- package/src/components/ui/select.tsx +209 -0
- package/src/components/ui/separator.tsx +45 -0
- package/src/components/ui/sheet.tsx +164 -0
- package/src/components/ui/sidebar.tsx +719 -0
- package/src/components/ui/skeleton.tsx +30 -0
- package/src/components/ui/sonner.tsx +37 -0
- package/src/components/ui/switch.tsx +48 -0
- package/src/components/ui/table.tsx +133 -0
- package/src/components/ui/textarea.tsx +35 -0
- package/src/components/ui/tooltip.tsx +74 -0
- package/src/components/user/columns.tsx +153 -0
- package/src/components/user/settings-activity-log.tsx +358 -0
- package/src/components/user/settings-profile/constants.ts +42 -0
- package/src/components/user/settings-profile/types.ts +47 -0
- package/src/components/user/settings-profile.tsx +664 -0
- package/src/components/user/user-create-dialog.tsx +137 -0
- package/src/components/user/user-filters.tsx +117 -0
- package/src/hooks/use-mobile.ts +36 -0
- package/src/instrument.client.ts +34 -0
- package/src/lib/anyhub/capabilities.ts +59 -0
- package/src/lib/anyhub/types.ts +43 -0
- package/src/lib/auth-client.ts +83 -0
- package/src/lib/auth-utils.ts +24 -0
- package/src/lib/auth.config.ts +264 -0
- package/src/lib/avatar.ts +41 -0
- package/src/lib/i18n/client.ts +32 -0
- package/src/lib/i18n/config.ts +40 -0
- package/src/lib/i18n/detect.ts +43 -0
- package/src/lib/i18n/locales/en/admin-ai.json +128 -0
- package/src/lib/i18n/locales/en/admin-billing.json +56 -0
- package/src/lib/i18n/locales/en/admin-users.json +88 -0
- package/src/lib/i18n/locales/en/common.json +877 -0
- package/src/lib/i18n/locales/en/enterprise.json +208 -0
- package/src/lib/i18n/locales/zh/admin-ai.json +128 -0
- package/src/lib/i18n/locales/zh/admin-billing.json +56 -0
- package/src/lib/i18n/locales/zh/admin-users.json +88 -0
- package/src/lib/i18n/locales/zh/common.json +877 -0
- package/src/lib/i18n/locales/zh/enterprise.json +208 -0
- package/src/lib/i18n/server.ts +18 -0
- package/src/lib/i18n/types.ts +5 -0
- package/src/lib/monitoring-benchmarks.ts +85 -0
- package/src/lib/monitoring-descriptions.ts +78 -0
- package/src/lib/prisma.ts +18 -0
- package/src/lib/router-context.ts +38 -0
- package/src/lib/session.ts +38 -0
- package/src/lib/utils.ts +23 -0
- package/src/providers/index.tsx +45 -0
- package/src/routeTree.gen.ts +1199 -0
- package/src/router.tsx +51 -0
- package/src/routes/__root.tsx +170 -0
- package/src/routes/_nav/ai-providers/$id/edit/route.lazy.tsx +74 -0
- package/src/routes/_nav/ai-providers/$id/edit/route.tsx +35 -0
- package/src/routes/_nav/ai-providers/index/route.lazy.tsx +33 -0
- package/src/routes/_nav/ai-providers/new/route.lazy.tsx +31 -0
- package/src/routes/_nav/ai-providers/new/route.tsx +20 -0
- package/src/routes/_nav/ai-providers/route.lazy.tsx +26 -0
- package/src/routes/_nav/ai-providers/route.tsx +31 -0
- package/src/routes/_nav/apps/route.lazy.tsx +30 -0
- package/src/routes/_nav/apps/route.tsx +20 -0
- package/src/routes/_nav/audit/route.lazy.tsx +100 -0
- package/src/routes/_nav/audit/route.tsx +25 -0
- package/src/routes/_nav/billing/invoices/route.lazy.tsx +86 -0
- package/src/routes/_nav/billing/invoices/route.tsx +25 -0
- package/src/routes/_nav/billing/route.lazy.tsx +48 -0
- package/src/routes/_nav/billing/route.tsx +27 -0
- package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.lazy.tsx +160 -0
- package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.tsx +25 -0
- package/src/routes/_nav/billing/subscriptions/route.lazy.tsx +113 -0
- package/src/routes/_nav/billing/subscriptions/route.tsx +28 -0
- package/src/routes/_nav/compliance/route.lazy.tsx +184 -0
- package/src/routes/_nav/compliance/route.tsx +25 -0
- package/src/routes/_nav/components/route.lazy.tsx +184 -0
- package/src/routes/_nav/components/route.tsx +40 -0
- package/src/routes/_nav/dashboard/route.lazy.tsx +33 -0
- package/src/routes/_nav/dashboard/route.tsx +20 -0
- package/src/routes/_nav/dependencies/route.lazy.tsx +66 -0
- package/src/routes/_nav/dependencies/route.tsx +32 -0
- package/src/routes/_nav/developer-logs/route.lazy.tsx +6 -0
- package/src/routes/_nav/developer-logs/route.tsx +14 -0
- package/src/routes/_nav/docs/route.lazy.tsx +30 -0
- package/src/routes/_nav/docs/route.tsx +20 -0
- package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +355 -0
- package/src/routes/_nav/knowledge-public/bookmarks/route.tsx +40 -0
- package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +245 -0
- package/src/routes/_nav/knowledge-public/compliance/route.tsx +40 -0
- package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +242 -0
- package/src/routes/_nav/knowledge-public/legal/route.tsx +40 -0
- package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +235 -0
- package/src/routes/_nav/licenses/$licenseId/route.tsx +25 -0
- package/src/routes/_nav/licenses/new/route.lazy.tsx +237 -0
- package/src/routes/_nav/licenses/route.lazy.tsx +143 -0
- package/src/routes/_nav/licenses/route.tsx +25 -0
- package/src/routes/_nav/market/route.lazy.tsx +31 -0
- package/src/routes/_nav/market/route.tsx +20 -0
- package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +305 -0
- package/src/routes/_nav/monitoring/ai-usage/route.tsx +3 -0
- package/src/routes/_nav/monitoring/errors/route.lazy.tsx +111 -0
- package/src/routes/_nav/monitoring/health/route.lazy.tsx +260 -0
- package/src/routes/_nav/monitoring/logs/route.lazy.tsx +165 -0
- package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +240 -0
- package/src/routes/_nav/monitoring/overview/route.lazy.tsx +138 -0
- package/src/routes/_nav/monitoring/route.lazy.tsx +68 -0
- package/src/routes/_nav/monitoring/route.tsx +20 -0
- package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +399 -0
- package/src/routes/_nav/monitoring/sentry/route.tsx +20 -0
- package/src/routes/_nav/permissions/route.lazy.tsx +504 -0
- package/src/routes/_nav/permissions/route.tsx +26 -0
- package/src/routes/_nav/route.lazy.tsx +31 -0
- package/src/routes/_nav/route.tsx +34 -0
- package/src/routes/_nav/settings/route.lazy.tsx +30 -0
- package/src/routes/_nav/settings/route.tsx +20 -0
- package/src/routes/_nav/sso/route.lazy.tsx +145 -0
- package/src/routes/_nav/sso/route.tsx +25 -0
- package/src/routes/_nav/users/$userId/route.lazy.tsx +432 -0
- package/src/routes/_nav/users/$userId/route.tsx +27 -0
- package/src/routes/_nav/users/-components/users-table.tsx +38 -0
- package/src/routes/_nav/users/route.lazy.tsx +74 -0
- package/src/routes/_nav/users/route.tsx +43 -0
- package/src/routes/api/compliance-download/$jobId.ts +35 -0
- package/src/routes/api/observability/errors.ts +41 -0
- package/src/routes/api/observability/health.ts +30 -0
- package/src/routes/api.auth.$.ts +31 -0
- package/src/routes/api.sentry-test.ts +33 -0
- package/src/routes/index/route.lazy.tsx +22 -0
- package/src/routes/index/route.tsx +24 -0
- package/src/routes/login/route.lazy.tsx +46 -0
- package/src/routes/login/route.tsx +28 -0
- package/src/routes/sentry-tunnel.ts +61 -0
- package/src/routes/setup/route.lazy.tsx +94 -0
- package/src/routes/setup/route.tsx +28 -0
- package/src/server/__tests__/admin-ai-alerts.test.ts +113 -0
- package/src/server/__tests__/licenses-issue.test.ts +98 -0
- package/src/server/admin-ai-alerts.ts +177 -0
- package/src/server/admin-ai-usage.ts +134 -0
- package/src/server/auth-helpers.ts +51 -0
- package/src/server/entitlements.ts +24 -0
- package/src/server/knowledge-public/canonical-id.ts +55 -0
- package/src/server/knowledge-public/github-sync.ts +86 -0
- package/src/server/license-issue.ts +78 -0
- package/src/server/middlewares/request.ts +26 -0
- package/src/server/observability/debug-middleware.ts +39 -0
- package/src/server/observability/error-reporter.ts +99 -0
- package/src/server/observability/health-check.ts +140 -0
- package/src/server/observability/index.ts +33 -0
- package/src/server/observability/logger.ts +49 -0
- package/src/server/observability/metrics.ts +105 -0
- package/src/server/observability/request-middleware.ts +125 -0
- package/src/server/observability/timing.ts +42 -0
- package/src/server/pro-services.ts +32 -0
- package/src/server/serverFns/admin-billing.ts +94 -0
- package/src/server/serverFns/ai-provider/create.ts +92 -0
- package/src/server/serverFns/ai-provider/delete.ts +42 -0
- package/src/server/serverFns/ai-provider/get.ts +35 -0
- package/src/server/serverFns/ai-provider/list.ts +92 -0
- package/src/server/serverFns/ai-provider/update.ts +73 -0
- package/src/server/serverFns/anyhub/market.ts +64 -0
- package/src/server/serverFns/audit.ts +57 -0
- package/src/server/serverFns/auth/session.ts +26 -0
- package/src/server/serverFns/auth/setup.ts +73 -0
- package/src/server/serverFns/compliance.ts +85 -0
- package/src/server/serverFns/components/import.ts +197 -0
- package/src/server/serverFns/components/list.ts +51 -0
- package/src/server/serverFns/components/mutations.ts +84 -0
- package/src/server/serverFns/dependency/helpers.ts +245 -0
- package/src/server/serverFns/dependency/list.ts +127 -0
- package/src/server/serverFns/dependency/sync.ts +123 -0
- package/src/server/serverFns/dependency/update.ts +51 -0
- package/src/server/serverFns/developer-log-auto-generate.ts +176 -0
- package/src/server/serverFns/developer-logs.ts +132 -0
- package/src/server/serverFns/knowledge-public/bookmarks/create.ts +53 -0
- package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/bookmarks/list.ts +66 -0
- package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +56 -0
- package/src/server/serverFns/knowledge-public/bookmarks/update.ts +50 -0
- package/src/server/serverFns/knowledge-public/compliance/create.ts +58 -0
- package/src/server/serverFns/knowledge-public/compliance/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/compliance/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/compliance/list.ts +59 -0
- package/src/server/serverFns/knowledge-public/compliance/update.ts +53 -0
- package/src/server/serverFns/knowledge-public/legal/create.ts +62 -0
- package/src/server/serverFns/knowledge-public/legal/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/legal/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/legal/list.ts +60 -0
- package/src/server/serverFns/knowledge-public/legal/update.ts +55 -0
- package/src/server/serverFns/licenses.ts +92 -0
- package/src/server/serverFns/locale.ts +10 -0
- package/src/server/serverFns/monitoring/check-ai-alerts.ts +13 -0
- package/src/server/serverFns/monitoring/get-ai-usage-overview.ts +25 -0
- package/src/server/serverFns/monitoring/get-errors.ts +43 -0
- package/src/server/serverFns/monitoring/get-health-settings.ts +35 -0
- package/src/server/serverFns/monitoring/get-health.ts +55 -0
- package/src/server/serverFns/monitoring/get-metrics.ts +46 -0
- package/src/server/serverFns/monitoring/get-request-logs.ts +66 -0
- package/src/server/serverFns/monitoring/report-error.ts +55 -0
- package/src/server/serverFns/monitoring/reset.ts +70 -0
- package/src/server/serverFns/permissions.ts +214 -0
- package/src/server/serverFns/sentry/config.ts +88 -0
- package/src/server/serverFns/sso-config.ts +83 -0
- package/src/server/serverFns/user/create.ts +40 -0
- package/src/server/serverFns/user/delete.ts +29 -0
- package/src/server/serverFns/user/detail.ts +45 -0
- package/src/server/serverFns/user/list.ts +56 -0
- package/src/server/serverFns/user/reset-password.ts +28 -0
- package/src/server/serverFns/user/update-role.ts +26 -0
- package/src/server/serverFns/user/update-status.ts +26 -0
- package/src/server.ts +27 -0
- package/src/start.ts +85 -0
- package/src/styles/_keyframe-animations.scss +91 -0
- package/src/styles/_variables.scss +296 -0
- package/src/styles/globals.css +200 -0
- package/src/types/start-authjs.d.ts +27 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
"use client"
|
|
19
|
+
|
|
20
|
+
import { useMemo } from "react"
|
|
21
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
22
|
+
|
|
23
|
+
import { cn } from "@/lib/utils"
|
|
24
|
+
import { Label } from "@/components/ui/label"
|
|
25
|
+
import { Separator } from "@/components/ui/separator"
|
|
26
|
+
|
|
27
|
+
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
28
|
+
return (
|
|
29
|
+
<fieldset
|
|
30
|
+
data-slot="field-set"
|
|
31
|
+
className={cn(
|
|
32
|
+
"flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function FieldLegend({
|
|
41
|
+
className,
|
|
42
|
+
variant = "legend",
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
|
|
45
|
+
return (
|
|
46
|
+
<legend
|
|
47
|
+
data-slot="field-legend"
|
|
48
|
+
data-variant={variant}
|
|
49
|
+
className={cn(
|
|
50
|
+
"mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
|
|
51
|
+
className
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
data-slot="field-group"
|
|
62
|
+
className={cn(
|
|
63
|
+
"group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const fieldVariants = cva(
|
|
72
|
+
"group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
|
|
73
|
+
{
|
|
74
|
+
variants: {
|
|
75
|
+
orientation: {
|
|
76
|
+
vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
|
|
77
|
+
horizontal:
|
|
78
|
+
"flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
79
|
+
responsive:
|
|
80
|
+
"flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
defaultVariants: {
|
|
84
|
+
orientation: "vertical",
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
function Field({
|
|
90
|
+
className,
|
|
91
|
+
orientation = "vertical",
|
|
92
|
+
...props
|
|
93
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
role="group"
|
|
97
|
+
data-slot="field"
|
|
98
|
+
data-orientation={orientation}
|
|
99
|
+
className={cn(fieldVariants({ orientation }), className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
data-slot="field-content"
|
|
109
|
+
className={cn(
|
|
110
|
+
"group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function FieldLabel({
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}: React.ComponentProps<typeof Label>) {
|
|
122
|
+
return (
|
|
123
|
+
<Label
|
|
124
|
+
data-slot="field-label"
|
|
125
|
+
className={cn(
|
|
126
|
+
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
|
|
127
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
|
|
128
|
+
className
|
|
129
|
+
)}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
data-slot="field-label"
|
|
139
|
+
className={cn(
|
|
140
|
+
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
|
|
141
|
+
className
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
149
|
+
return (
|
|
150
|
+
<p
|
|
151
|
+
data-slot="field-description"
|
|
152
|
+
className={cn(
|
|
153
|
+
"text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
|
|
154
|
+
"last:mt-0 nth-last-2:-mt-1",
|
|
155
|
+
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
156
|
+
className
|
|
157
|
+
)}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function FieldSeparator({
|
|
164
|
+
children,
|
|
165
|
+
className,
|
|
166
|
+
...props
|
|
167
|
+
}: React.ComponentProps<"div"> & {
|
|
168
|
+
children?: React.ReactNode
|
|
169
|
+
}) {
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
data-slot="field-separator"
|
|
173
|
+
data-content={!!children}
|
|
174
|
+
className={cn(
|
|
175
|
+
"relative -my-2 h-0.5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
|
176
|
+
className
|
|
177
|
+
)}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
<Separator className="absolute inset-0 top-1/2" />
|
|
181
|
+
{children && (
|
|
182
|
+
<span
|
|
183
|
+
className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
|
|
184
|
+
data-slot="field-separator-content"
|
|
185
|
+
>
|
|
186
|
+
{children}
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function FieldError({
|
|
194
|
+
className,
|
|
195
|
+
children,
|
|
196
|
+
errors,
|
|
197
|
+
...props
|
|
198
|
+
}: React.ComponentProps<"div"> & {
|
|
199
|
+
errors?: Array<{ message?: string } | undefined>
|
|
200
|
+
}) {
|
|
201
|
+
const content = useMemo(() => {
|
|
202
|
+
if (children) {
|
|
203
|
+
return children
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!errors?.length) {
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const uniqueErrors = [
|
|
211
|
+
...new Map(errors.map((error) => [error?.message, error])).values(),
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
if (uniqueErrors?.length == 1) {
|
|
215
|
+
return uniqueErrors[0]?.message
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
220
|
+
{uniqueErrors.map(
|
|
221
|
+
(error, index) =>
|
|
222
|
+
error?.message && <li key={index}>{error.message}</li>
|
|
223
|
+
)}
|
|
224
|
+
</ul>
|
|
225
|
+
)
|
|
226
|
+
}, [children, errors])
|
|
227
|
+
|
|
228
|
+
if (!content) {
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div
|
|
234
|
+
role="alert"
|
|
235
|
+
data-slot="field-error"
|
|
236
|
+
className={cn("text-sm font-normal text-destructive", className)}
|
|
237
|
+
{...props}
|
|
238
|
+
>
|
|
239
|
+
{content}
|
|
240
|
+
</div>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export {
|
|
245
|
+
Field,
|
|
246
|
+
FieldLabel,
|
|
247
|
+
FieldDescription,
|
|
248
|
+
FieldError,
|
|
249
|
+
FieldGroup,
|
|
250
|
+
FieldLegend,
|
|
251
|
+
FieldSeparator,
|
|
252
|
+
FieldSet,
|
|
253
|
+
FieldContent,
|
|
254
|
+
FieldTitle,
|
|
255
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import * as React from "react"
|
|
19
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
20
|
+
|
|
21
|
+
import { cn } from "@/lib/utils"
|
|
22
|
+
import { Button } from "@/components/ui/button"
|
|
23
|
+
import { Input } from "@/components/ui/input"
|
|
24
|
+
import { Textarea } from "@/components/ui/textarea"
|
|
25
|
+
|
|
26
|
+
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
data-slot="input-group"
|
|
30
|
+
role="group"
|
|
31
|
+
className={cn(
|
|
32
|
+
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const inputGroupAddonVariants = cva(
|
|
41
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
42
|
+
{
|
|
43
|
+
variants: {
|
|
44
|
+
align: {
|
|
45
|
+
"inline-start":
|
|
46
|
+
"order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]",
|
|
47
|
+
"inline-end":
|
|
48
|
+
"order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]",
|
|
49
|
+
"block-start":
|
|
50
|
+
"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
|
|
51
|
+
"block-end":
|
|
52
|
+
"order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
defaultVariants: {
|
|
56
|
+
align: "inline-start",
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
function InputGroupAddon({
|
|
62
|
+
className,
|
|
63
|
+
align = "inline-start",
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
role="group"
|
|
69
|
+
data-slot="input-group-addon"
|
|
70
|
+
data-align={align}
|
|
71
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
72
|
+
onClick={(e) => {
|
|
73
|
+
if ((e.target as HTMLElement).closest("button")) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus()
|
|
77
|
+
}}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const inputGroupButtonVariants = cva(
|
|
84
|
+
"flex items-center gap-2 text-sm shadow-none",
|
|
85
|
+
{
|
|
86
|
+
variants: {
|
|
87
|
+
size: {
|
|
88
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
89
|
+
sm: "",
|
|
90
|
+
"icon-xs":
|
|
91
|
+
"size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",
|
|
92
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
defaultVariants: {
|
|
96
|
+
size: "xs",
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
function InputGroupButton({
|
|
102
|
+
className,
|
|
103
|
+
type = "button",
|
|
104
|
+
variant = "ghost",
|
|
105
|
+
size = "xs",
|
|
106
|
+
...props
|
|
107
|
+
}: Omit<React.ComponentProps<typeof Button>, "size"> &
|
|
108
|
+
VariantProps<typeof inputGroupButtonVariants>) {
|
|
109
|
+
return (
|
|
110
|
+
<Button
|
|
111
|
+
type={type}
|
|
112
|
+
data-size={size}
|
|
113
|
+
variant={variant}
|
|
114
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
121
|
+
return (
|
|
122
|
+
<span
|
|
123
|
+
className={cn(
|
|
124
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
125
|
+
className
|
|
126
|
+
)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function InputGroupInput({
|
|
133
|
+
className,
|
|
134
|
+
...props
|
|
135
|
+
}: React.ComponentProps<"input">) {
|
|
136
|
+
return (
|
|
137
|
+
<Input
|
|
138
|
+
data-slot="input-group-control"
|
|
139
|
+
className={cn(
|
|
140
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
141
|
+
className
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function InputGroupTextarea({
|
|
149
|
+
className,
|
|
150
|
+
...props
|
|
151
|
+
}: React.ComponentProps<"textarea">) {
|
|
152
|
+
return (
|
|
153
|
+
<Textarea
|
|
154
|
+
data-slot="input-group-control"
|
|
155
|
+
className={cn(
|
|
156
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
157
|
+
className
|
|
158
|
+
)}
|
|
159
|
+
{...props}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
InputGroup,
|
|
166
|
+
InputGroupAddon,
|
|
167
|
+
InputGroupButton,
|
|
168
|
+
InputGroupText,
|
|
169
|
+
InputGroupInput,
|
|
170
|
+
InputGroupTextarea,
|
|
171
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import * as React from "react"
|
|
19
|
+
|
|
20
|
+
import { cn } from "@/lib/utils"
|
|
21
|
+
|
|
22
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
23
|
+
return (
|
|
24
|
+
<input
|
|
25
|
+
type={type}
|
|
26
|
+
data-slot="input"
|
|
27
|
+
className={cn(
|
|
28
|
+
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { Input }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
"use client"
|
|
19
|
+
|
|
20
|
+
import * as React from "react"
|
|
21
|
+
import { Label as LabelPrimitive } from "radix-ui"
|
|
22
|
+
|
|
23
|
+
import { cn } from "@/lib/utils"
|
|
24
|
+
|
|
25
|
+
function Label({
|
|
26
|
+
className,
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
29
|
+
return (
|
|
30
|
+
<LabelPrimitive.Root
|
|
31
|
+
data-slot="label"
|
|
32
|
+
className={cn(
|
|
33
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { Label }
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import * as React from "react"
|
|
19
|
+
|
|
20
|
+
import { cn } from "@/lib/utils"
|
|
21
|
+
import { Button } from "@/components/ui/button"
|
|
22
|
+
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
23
|
+
|
|
24
|
+
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
|
25
|
+
return (
|
|
26
|
+
<nav
|
|
27
|
+
role="navigation"
|
|
28
|
+
aria-label="pagination"
|
|
29
|
+
data-slot="pagination"
|
|
30
|
+
className={cn("mx-auto flex w-full justify-center", className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function PaginationContent({
|
|
37
|
+
className,
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<"ul">) {
|
|
40
|
+
return (
|
|
41
|
+
<ul
|
|
42
|
+
data-slot="pagination-content"
|
|
43
|
+
className={cn("flex items-center gap-0.5", className)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
|
|
50
|
+
return <li data-slot="pagination-item" {...props} />
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type PaginationLinkProps = {
|
|
54
|
+
isActive?: boolean
|
|
55
|
+
} & Pick<React.ComponentProps<typeof Button>, "size"> &
|
|
56
|
+
React.ComponentProps<"a">
|
|
57
|
+
|
|
58
|
+
function PaginationLink({
|
|
59
|
+
className,
|
|
60
|
+
isActive,
|
|
61
|
+
size = "icon",
|
|
62
|
+
...props
|
|
63
|
+
}: PaginationLinkProps) {
|
|
64
|
+
return (
|
|
65
|
+
<Button
|
|
66
|
+
asChild
|
|
67
|
+
variant={isActive ? "outline" : "ghost"}
|
|
68
|
+
size={size}
|
|
69
|
+
className={cn(className)}
|
|
70
|
+
>
|
|
71
|
+
<a
|
|
72
|
+
aria-current={isActive ? "page" : undefined}
|
|
73
|
+
data-slot="pagination-link"
|
|
74
|
+
data-active={isActive}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
</Button>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function PaginationPrevious({
|
|
82
|
+
className,
|
|
83
|
+
text = "Previous",
|
|
84
|
+
...props
|
|
85
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
86
|
+
return (
|
|
87
|
+
<PaginationLink
|
|
88
|
+
aria-label="Go to previous page"
|
|
89
|
+
size="default"
|
|
90
|
+
className={cn("pl-1.5!", className)}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
<ChevronLeftIcon data-icon="inline-start" />
|
|
94
|
+
<span className="hidden sm:block">{text}</span>
|
|
95
|
+
</PaginationLink>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function PaginationNext({
|
|
100
|
+
className,
|
|
101
|
+
text = "Next",
|
|
102
|
+
...props
|
|
103
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
104
|
+
return (
|
|
105
|
+
<PaginationLink
|
|
106
|
+
aria-label="Go to next page"
|
|
107
|
+
size="default"
|
|
108
|
+
className={cn("pr-1.5!", className)}
|
|
109
|
+
{...props}
|
|
110
|
+
>
|
|
111
|
+
<span className="hidden sm:block">{text}</span>
|
|
112
|
+
<ChevronRightIcon data-icon="inline-end" />
|
|
113
|
+
</PaginationLink>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function PaginationEllipsis({
|
|
118
|
+
className,
|
|
119
|
+
...props
|
|
120
|
+
}: React.ComponentProps<"span">) {
|
|
121
|
+
return (
|
|
122
|
+
<span
|
|
123
|
+
aria-hidden
|
|
124
|
+
data-slot="pagination-ellipsis"
|
|
125
|
+
className={cn(
|
|
126
|
+
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
|
|
127
|
+
className
|
|
128
|
+
)}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
<MoreHorizontalIcon
|
|
132
|
+
/>
|
|
133
|
+
<span className="sr-only">More pages</span>
|
|
134
|
+
</span>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export {
|
|
139
|
+
Pagination,
|
|
140
|
+
PaginationContent,
|
|
141
|
+
PaginationEllipsis,
|
|
142
|
+
PaginationItem,
|
|
143
|
+
PaginationLink,
|
|
144
|
+
PaginationNext,
|
|
145
|
+
PaginationPrevious,
|
|
146
|
+
}
|