@allbluecn/admin-app 0.3.3 → 0.3.5
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 +4 -3
- package/src/components/admin/admin-user-menu.tsx +4 -4
- package/src/components/admin/developer-log.tsx +7 -7
- package/src/components/ai-memory/memory-facts-panel.tsx +9 -9
- package/src/components/ai-prompts/preview-panel.tsx +1 -1
- package/src/components/ai-prompts/prompt-editor.tsx +8 -8
- package/src/components/ai-prompts/prompt-table.tsx +6 -6
- package/src/components/ai-prompts/section-editor.tsx +3 -3
- package/src/components/ai-prompts/variable-panel.tsx +2 -2
- package/src/components/ai-prompts/version-history.tsx +5 -5
- package/src/components/ai-provider/model-list-editor.tsx +2 -2
- package/src/components/ai-provider/provider-form.tsx +7 -7
- package/src/components/ai-provider/provider-table.tsx +6 -6
- package/src/components/auth/admin-login-form.tsx +6 -6
- package/src/components/auth/icon-input.tsx +2 -2
- package/src/components/auth/password-input.tsx +2 -2
- package/src/components/component/component-form-dialog.tsx +8 -8
- package/src/components/component/component-import-dialog.tsx +6 -6
- package/src/components/component/component-table.tsx +3 -3
- package/src/components/confirm-dialog.tsx +94 -0
- package/src/components/data-table/data-table-pagination.tsx +121 -0
- package/src/components/data-table/data-table.tsx +218 -0
- package/src/components/data-table/table-detail-layout.tsx +97 -0
- package/src/components/dependency/changelog-dialog.tsx +2 -2
- package/src/components/dependency/columns.tsx +3 -3
- package/src/components/dependency/dependency-table.tsx +7 -7
- package/src/components/dependency/edit-dialog.tsx +8 -8
- package/src/components/invitations/invitations-page.tsx +13 -13
- package/src/components/knowledge-public/bookmark-form-dialog.tsx +6 -6
- package/src/components/knowledge-public/compliance-form-dialog.tsx +6 -6
- package/src/components/knowledge-public/legal-form-dialog.tsx +7 -7
- package/src/components/layout/sidebar-02/app-sidebar.tsx +1 -1
- package/src/components/layout/sidebar-02/index.tsx +1 -1
- package/src/components/layout/sidebar-02/nav-main.tsx +2 -2
- package/src/components/layout/sidebar-02/nav-notifications.tsx +3 -3
- package/src/components/market/columns.tsx +4 -4
- package/src/components/market/market-app-form.tsx +6 -6
- package/src/components/market/market-app-list.tsx +4 -4
- package/src/components/monitoring/error-list.tsx +1 -1
- package/src/components/monitoring/metrics-stats.tsx +1 -1
- package/src/components/monitoring/request-log-table.tsx +1 -1
- package/src/components/notion-avatar.tsx +126 -0
- package/src/components/oauth-providers/provider-page.tsx +9 -9
- package/src/components/permissions/assign-dialog.tsx +5 -5
- package/src/components/permissions/create-role-dialog.tsx +6 -6
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +75 -0
- package/src/components/ui/avatar.tsx +106 -0
- package/src/components/ui/badge.tsx +51 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/card.tsx +102 -0
- package/src/components/ui/checkbox.tsx +27 -0
- package/src/components/ui/collapsible.tsx +19 -0
- package/src/components/ui/dialog.tsx +155 -0
- package/src/components/ui/dropdown-menu.tsx +265 -0
- package/src/components/ui/field.tsx +236 -0
- package/src/components/ui/input-group.tsx +157 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +17 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/select.tsx +232 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +134 -0
- package/src/components/ui/sidebar.tsx +721 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/switch.tsx +30 -0
- package/src/components/ui/table.tsx +113 -0
- package/src/components/ui/textarea.tsx +17 -0
- package/src/components/ui/toast.tsx +308 -0
- package/src/components/ui/tooltip.tsx +64 -0
- package/src/components/user/columns.tsx +4 -4
- package/src/components/user/settings-activity-log.tsx +7 -7
- package/src/components/user/settings-profile/avatar-uploader.tsx +2 -2
- package/src/components/user/settings-profile/social-links.tsx +2 -2
- package/src/components/user/settings-profile.tsx +6 -6
- package/src/components/user/user-create-dialog.tsx +6 -6
- package/src/components/user/user-detail/error-boundary.tsx +1 -1
- package/src/components/user/user-detail/user-detail-dialogs.tsx +6 -6
- package/src/components/user/user-filters.tsx +3 -3
- package/src/components/user-avatar.tsx +82 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/datetime.ts +226 -0
- package/src/lib/sanitize.ts +18 -0
- package/src/providers/index.tsx +1 -1
- package/src/routes/__root.tsx +1 -1
- package/src/routes/_nav/audit/route.lazy.tsx +1 -1
- package/src/routes/_nav/compliance/route.lazy.tsx +3 -3
- package/src/routes/_nav/components/route.lazy.tsx +4 -4
- package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +6 -6
- package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +6 -6
- package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +6 -6
- package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +3 -3
- package/src/routes/_nav/licenses/new/route.lazy.tsx +5 -5
- package/src/routes/_nav/licenses/route.lazy.tsx +1 -1
- package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +2 -2
- package/src/routes/_nav/monitoring/errors/route.lazy.tsx +1 -1
- package/src/routes/_nav/monitoring/health/route.lazy.tsx +3 -3
- package/src/routes/_nav/monitoring/logs/route.lazy.tsx +3 -3
- package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +3 -3
- package/src/routes/_nav/monitoring/route.lazy.tsx +2 -2
- package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +1 -1
- package/src/routes/_nav/permissions/route.lazy.tsx +4 -4
- package/src/routes/_nav/sso/route.lazy.tsx +2 -2
- package/src/routes/_nav/users/$userId/route.lazy.tsx +8 -8
- package/src/routes/_nav/users/-components/users-table.tsx +1 -1
- package/src/routes/_nav/users/route.lazy.tsx +1 -1
- package/src/routes/setup/route.lazy.tsx +2 -2
- package/src/server/dependency/helpers.ts +0 -1
- package/src/styles/globals.css +3 -3
|
@@ -25,18 +25,18 @@ import {
|
|
|
25
25
|
DialogFooter,
|
|
26
26
|
DialogHeader,
|
|
27
27
|
DialogTitle,
|
|
28
|
-
} from "
|
|
29
|
-
import { Button } from "
|
|
30
|
-
import { Input } from "
|
|
31
|
-
import { Label } from "
|
|
28
|
+
} from "@/components/ui/dialog"
|
|
29
|
+
import { Button } from "@/components/ui/button"
|
|
30
|
+
import { Input } from "@/components/ui/input"
|
|
31
|
+
import { Label } from "@/components/ui/label"
|
|
32
32
|
import {
|
|
33
33
|
Select,
|
|
34
34
|
SelectContent,
|
|
35
35
|
SelectItem,
|
|
36
36
|
SelectTrigger,
|
|
37
37
|
SelectValue,
|
|
38
|
-
} from "
|
|
39
|
-
import { toast } from '
|
|
38
|
+
} from "@/components/ui/select"
|
|
39
|
+
import { toast } from '@/components/ui/toast'
|
|
40
40
|
|
|
41
41
|
interface MarketAppFormProps {
|
|
42
42
|
open: boolean
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
import { useCallback, useEffect, useState } from "react"
|
|
19
19
|
import { m } from '@/paraglide/messages.js'
|
|
20
|
-
import { DataTable } from "
|
|
20
|
+
import { DataTable } from "@/components/data-table/data-table"
|
|
21
21
|
import { getMarketColumns, MarketAppRow } from "./columns"
|
|
22
22
|
import { MarketAppForm } from "./market-app-form"
|
|
23
23
|
import { MarketAppFormData } from "@/lib/anyhub/types"
|
|
24
|
-
import { Button } from "
|
|
24
|
+
import { Button } from "@/components/ui/button"
|
|
25
25
|
import { PlusIcon } from "lucide-react"
|
|
26
|
-
import { toast } from '
|
|
26
|
+
import { toast } from '@/components/ui/toast'
|
|
27
27
|
import { listMarketAppsFn, updateMarketAppFn, deleteMarketAppFn } from "@/server/serverFns/anyhub/market"
|
|
28
|
-
import { ConfirmDialog } from "
|
|
28
|
+
import { ConfirmDialog } from "@/components/confirm-dialog"
|
|
29
29
|
|
|
30
30
|
export function MarketAppList() {
|
|
31
31
|
const [apps, setApps] = useState<MarketAppRow[]>([])
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { useState } from 'react'
|
|
19
19
|
import { m } from '@/paraglide/messages.js'
|
|
20
|
-
import { DataTable, type AdminColumnDef } from '
|
|
20
|
+
import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
|
|
21
21
|
|
|
22
22
|
function formatRelativeTime(dateStr: string): string {
|
|
23
23
|
const now = new Date()
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
import { DataTable, type AdminColumnDef } from '
|
|
18
|
+
import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
|
|
19
19
|
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { describeMetric } from '@/lib/monitoring-descriptions'
|
|
21
21
|
import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import type React from 'react'
|
|
19
19
|
import { m } from '@/paraglide/messages.js'
|
|
20
|
-
import { DataTable, type AdminColumnDef } from '
|
|
20
|
+
import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
|
|
21
21
|
import { describePath } from '@/lib/monitoring-descriptions'
|
|
22
22
|
import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
|
|
23
23
|
import { ArrowDown, ArrowUp, Info } from 'lucide-react'
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
|
|
19
|
+
import { useEffect, useState } from "react"
|
|
20
|
+
import type { AvatarConfig, AvatarPart, ShapeType } from "@allbluecn/kernel"
|
|
21
|
+
import { sanitizeSvg } from "../lib/sanitize"
|
|
22
|
+
|
|
23
|
+
const noneParts: AvatarPart[] = ["glasses", "hair", "accessories", "details", "beard"]
|
|
24
|
+
|
|
25
|
+
const partOrder: AvatarPart[] = [
|
|
26
|
+
"face",
|
|
27
|
+
"nose",
|
|
28
|
+
"mouth",
|
|
29
|
+
"eyes",
|
|
30
|
+
"eyebrows",
|
|
31
|
+
"glasses",
|
|
32
|
+
"hair",
|
|
33
|
+
"accessories",
|
|
34
|
+
"details",
|
|
35
|
+
"beard",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
function getPreviewImagePath(part: AvatarPart, index: number): string {
|
|
39
|
+
return `/avatar/preview/${part}/${index}.svg`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const SVGFilter = `<defs>
|
|
43
|
+
<filter id="notion-avatar-filter" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="linearRGB">
|
|
44
|
+
<feMorphology operator="dilate" radius="20" in="SourceAlpha" result="morphology"/>
|
|
45
|
+
<feFlood flood-color="#ffffff" flood-opacity="1" result="flood"/>
|
|
46
|
+
<feComposite in="flood" in2="morphology" operator="in" result="composite"/>
|
|
47
|
+
<feMerge result="merge">
|
|
48
|
+
<feMergeNode in="composite" result="mergeNode"/>
|
|
49
|
+
<feMergeNode in="SourceGraphic" result="mergeNode1"/>
|
|
50
|
+
</feMerge>
|
|
51
|
+
</filter>
|
|
52
|
+
</defs>`
|
|
53
|
+
|
|
54
|
+
function extractInnerSvg(svgText: string): string {
|
|
55
|
+
return svgText.replace(/<svg[^>]*>/i, "").replace(/<\/svg>/i, "").trim()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const shapeStyle: Record<ShapeType, string> = {
|
|
59
|
+
circle: "rounded-full",
|
|
60
|
+
rounded: "rounded-lg",
|
|
61
|
+
square: "rounded-none",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type NotionAvatarProps = {
|
|
65
|
+
config: AvatarConfig
|
|
66
|
+
bgColor?: string
|
|
67
|
+
shape?: ShapeType
|
|
68
|
+
className?: string
|
|
69
|
+
style?: React.CSSProperties
|
|
70
|
+
flip?: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function NotionAvatar({
|
|
74
|
+
config,
|
|
75
|
+
bgColor = "transparent",
|
|
76
|
+
shape = "circle",
|
|
77
|
+
className,
|
|
78
|
+
style,
|
|
79
|
+
flip = false,
|
|
80
|
+
}: NotionAvatarProps) {
|
|
81
|
+
const [svgContent, setSvgContent] = useState<string>("")
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
async function buildAvatar() {
|
|
85
|
+
const groups = await Promise.all(
|
|
86
|
+
partOrder.map(async (part) => {
|
|
87
|
+
const index = config[part]
|
|
88
|
+
if (noneParts.includes(part) && index === 0) return ""
|
|
89
|
+
try {
|
|
90
|
+
const res = await fetch(getPreviewImagePath(part, index))
|
|
91
|
+
const svgText = await res.text()
|
|
92
|
+
const inner = extractInnerSvg(svgText)
|
|
93
|
+
return `\n<g id="avatar-${part}"${part === "face" ? ' fill="#ffffff"' : ""}>${inner}</g>`
|
|
94
|
+
} catch {
|
|
95
|
+
return ""
|
|
96
|
+
}
|
|
97
|
+
}),
|
|
98
|
+
)
|
|
99
|
+
const flipAttr = flip ? ' transform="scale(-1,1) translate(-1080, 0)"' : ""
|
|
100
|
+
const avatarSvg = `<svg viewBox="0 0 1080 1080" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%">${SVGFilter}<g id="avatar"${flipAttr}>${groups.join("")}</g></svg>`
|
|
101
|
+
setSvgContent(avatarSvg)
|
|
102
|
+
}
|
|
103
|
+
buildAvatar()
|
|
104
|
+
}, [config, flip])
|
|
105
|
+
|
|
106
|
+
if (!svgContent) {
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
className={`${shapeStyle[shape]} overflow-hidden bg-muted animate-pulse ${className ?? ""}`}
|
|
110
|
+
style={{ aspectRatio: "1 / 1", ...style }}
|
|
111
|
+
/>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
className={`${shapeStyle[shape]} ${className ?? ""}`}
|
|
118
|
+
style={{
|
|
119
|
+
backgroundColor: bgColor === "transparent" ? undefined : bgColor,
|
|
120
|
+
aspectRatio: "1 / 1",
|
|
121
|
+
...style,
|
|
122
|
+
}}
|
|
123
|
+
dangerouslySetInnerHTML={{ __html: sanitizeSvg(svgContent) }}
|
|
124
|
+
/>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
@@ -19,17 +19,17 @@ import { useEffect, useState } from 'react'
|
|
|
19
19
|
import type React from 'react'
|
|
20
20
|
import { m } from '@/paraglide/messages.js'
|
|
21
21
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
22
|
-
import { toast } from '
|
|
22
|
+
import { toast } from '@/components/ui/toast'
|
|
23
23
|
import { Eye, EyeOff, KeyRound, PlugZap, Save, Trash2 } from 'lucide-react'
|
|
24
24
|
import { PageHeader } from '@/components/common/page-header'
|
|
25
|
-
import { Button } from '
|
|
26
|
-
import { Badge } from '
|
|
27
|
-
import { Card, CardContent } from '
|
|
28
|
-
import { Input } from '
|
|
29
|
-
import { Label } from '
|
|
30
|
-
import { Switch } from '
|
|
31
|
-
import { Skeleton } from '
|
|
32
|
-
import { ConfirmDialog } from '
|
|
25
|
+
import { Button } from '@/components/ui/button'
|
|
26
|
+
import { Badge } from '@/components/ui/badge'
|
|
27
|
+
import { Card, CardContent } from '@/components/ui/card'
|
|
28
|
+
import { Input } from '@/components/ui/input'
|
|
29
|
+
import { Label } from '@/components/ui/label'
|
|
30
|
+
import { Switch } from '@/components/ui/switch'
|
|
31
|
+
import { Skeleton } from '@/components/ui/skeleton'
|
|
32
|
+
import { ConfirmDialog } from '@/components/confirm-dialog'
|
|
33
33
|
import {
|
|
34
34
|
deleteOAuthProviderFn,
|
|
35
35
|
listOAuthProvidersFn,
|
|
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
import { m } from '@/paraglide/messages.js'
|
|
4
4
|
import { useServerFn } from '@tanstack/react-start'
|
|
5
|
-
import { toast } from '
|
|
5
|
+
import { toast } from '@/components/ui/toast'
|
|
6
6
|
import {
|
|
7
7
|
assignRoleFn,
|
|
8
8
|
unassignRoleFn,
|
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
DialogFooter,
|
|
16
16
|
DialogHeader,
|
|
17
17
|
DialogTitle,
|
|
18
|
-
} from '
|
|
19
|
-
import { Button } from '
|
|
20
|
-
import { Input } from '
|
|
18
|
+
} from '@/components/ui/dialog'
|
|
19
|
+
import { Button } from '@/components/ui/button'
|
|
20
|
+
import { Input } from '@/components/ui/input'
|
|
21
21
|
import {
|
|
22
22
|
Table,
|
|
23
23
|
TableBody,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
TableHead,
|
|
26
26
|
TableHeader,
|
|
27
27
|
TableRow,
|
|
28
|
-
} from '
|
|
28
|
+
} from '@/components/ui/table'
|
|
29
29
|
import type { AssignmentItem, RoleItem } from './types'
|
|
30
30
|
|
|
31
31
|
export function AssignDialog({
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
import { m } from '@/paraglide/messages.js'
|
|
4
4
|
import { useServerFn } from '@tanstack/react-start'
|
|
5
|
-
import { toast } from '
|
|
5
|
+
import { toast } from '@/components/ui/toast'
|
|
6
6
|
import { createRoleFn } from '@/server/serverFns/permissions'
|
|
7
7
|
import {
|
|
8
8
|
Dialog,
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
DialogFooter,
|
|
12
12
|
DialogHeader,
|
|
13
13
|
DialogTitle,
|
|
14
|
-
} from '
|
|
15
|
-
import { Button } from '
|
|
16
|
-
import { Input } from '
|
|
17
|
-
import { Label } from '
|
|
14
|
+
} from '@/components/ui/dialog'
|
|
15
|
+
import { Button } from '@/components/ui/button'
|
|
16
|
+
import { Input } from '@/components/ui/input'
|
|
17
|
+
import { Label } from '@/components/ui/label'
|
|
18
18
|
import {
|
|
19
19
|
Select,
|
|
20
20
|
SelectContent,
|
|
21
21
|
SelectItem,
|
|
22
22
|
SelectTrigger,
|
|
23
23
|
SelectValue,
|
|
24
|
-
} from '
|
|
24
|
+
} from '@/components/ui/select'
|
|
25
25
|
import { ALL_ACTIONS, ALL_RESOURCES, LEVELS, type ResourcePermission } from './types'
|
|
26
26
|
|
|
27
27
|
export function CreateRoleDialog({
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
import { Button } from "./button"
|
|
6
|
+
|
|
7
|
+
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
|
8
|
+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
|
|
12
|
+
return (
|
|
13
|
+
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
|
|
18
|
+
return (
|
|
19
|
+
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function AlertDialogOverlay({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: AlertDialogPrimitive.Backdrop.Props) {
|
|
27
|
+
return (
|
|
28
|
+
<AlertDialogPrimitive.Backdrop
|
|
29
|
+
data-slot="alert-dialog-overlay"
|
|
30
|
+
className={cn(
|
|
31
|
+
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function AlertDialogContent({
|
|
40
|
+
className,
|
|
41
|
+
size = "default",
|
|
42
|
+
...props
|
|
43
|
+
}: AlertDialogPrimitive.Popup.Props & {
|
|
44
|
+
size?: "default" | "sm"
|
|
45
|
+
}) {
|
|
46
|
+
return (
|
|
47
|
+
<AlertDialogPortal>
|
|
48
|
+
<AlertDialogOverlay />
|
|
49
|
+
<AlertDialogPrimitive.Popup
|
|
50
|
+
data-slot="alert-dialog-content"
|
|
51
|
+
data-size={size}
|
|
52
|
+
className={cn(
|
|
53
|
+
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-xl bg-popover p-6 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
</AlertDialogPortal>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function AlertDialogHeader({
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<"div">) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="alert-dialog-header"
|
|
69
|
+
className={cn(
|
|
70
|
+
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function AlertDialogFooter({
|
|
79
|
+
className,
|
|
80
|
+
...props
|
|
81
|
+
}: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="alert-dialog-footer"
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function AlertDialogMedia({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<"div">) {
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
data-slot="alert-dialog-media"
|
|
101
|
+
className={cn(
|
|
102
|
+
"mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function AlertDialogTitle({
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
114
|
+
return (
|
|
115
|
+
<AlertDialogPrimitive.Title
|
|
116
|
+
data-slot="alert-dialog-title"
|
|
117
|
+
className={cn(
|
|
118
|
+
"font-heading text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function AlertDialogDescription({
|
|
127
|
+
className,
|
|
128
|
+
...props
|
|
129
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
130
|
+
return (
|
|
131
|
+
<AlertDialogPrimitive.Description
|
|
132
|
+
data-slot="alert-dialog-description"
|
|
133
|
+
className={cn(
|
|
134
|
+
"text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
135
|
+
className
|
|
136
|
+
)}
|
|
137
|
+
{...props}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function AlertDialogAction({
|
|
143
|
+
className,
|
|
144
|
+
...props
|
|
145
|
+
}: React.ComponentProps<typeof Button>) {
|
|
146
|
+
return (
|
|
147
|
+
<Button
|
|
148
|
+
data-slot="alert-dialog-action"
|
|
149
|
+
className={cn(className)}
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function AlertDialogCancel({
|
|
156
|
+
className,
|
|
157
|
+
variant = "outline",
|
|
158
|
+
size = "default",
|
|
159
|
+
...props
|
|
160
|
+
}: AlertDialogPrimitive.Close.Props &
|
|
161
|
+
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
|
|
162
|
+
return (
|
|
163
|
+
<AlertDialogPrimitive.Close
|
|
164
|
+
data-slot="alert-dialog-cancel"
|
|
165
|
+
className={cn(className)}
|
|
166
|
+
render={<Button variant={variant} size={size} />}
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export {
|
|
173
|
+
AlertDialog,
|
|
174
|
+
AlertDialogAction,
|
|
175
|
+
AlertDialogCancel,
|
|
176
|
+
AlertDialogContent,
|
|
177
|
+
AlertDialogDescription,
|
|
178
|
+
AlertDialogFooter,
|
|
179
|
+
AlertDialogHeader,
|
|
180
|
+
AlertDialogMedia,
|
|
181
|
+
AlertDialogOverlay,
|
|
182
|
+
AlertDialogPortal,
|
|
183
|
+
AlertDialogTitle,
|
|
184
|
+
AlertDialogTrigger,
|
|
185
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
const alertVariants = cva(
|
|
6
|
+
"group/alert relative grid w-full gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-card text-card-foreground",
|
|
11
|
+
destructive:
|
|
12
|
+
"bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: "default",
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
function Alert({
|
|
22
|
+
className,
|
|
23
|
+
variant,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
data-slot="alert"
|
|
29
|
+
role="alert"
|
|
30
|
+
className={cn(alertVariants({ variant }), className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="alert-title"
|
|
40
|
+
className={cn(
|
|
41
|
+
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function AlertDescription({
|
|
50
|
+
className,
|
|
51
|
+
...props
|
|
52
|
+
}: React.ComponentProps<"div">) {
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
data-slot="alert-description"
|
|
56
|
+
className={cn(
|
|
57
|
+
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="alert-action"
|
|
69
|
+
className={cn("absolute top-2.5 right-3", className)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Alert, AlertTitle, AlertDescription, AlertAction }
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
function Avatar({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: AvatarPrimitive.Root.Props & {
|
|
10
|
+
size?: "default" | "sm" | "lg"
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
data-size={size}
|
|
16
|
+
className={cn(
|
|
17
|
+
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
|
|
26
|
+
return (
|
|
27
|
+
<AvatarPrimitive.Image
|
|
28
|
+
data-slot="avatar-image"
|
|
29
|
+
className={cn(
|
|
30
|
+
"aspect-square size-full rounded-full object-cover",
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function AvatarFallback({
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}: AvatarPrimitive.Fallback.Props) {
|
|
42
|
+
return (
|
|
43
|
+
<AvatarPrimitive.Fallback
|
|
44
|
+
data-slot="avatar-fallback"
|
|
45
|
+
className={cn(
|
|
46
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
55
|
+
return (
|
|
56
|
+
<span
|
|
57
|
+
data-slot="avatar-badge"
|
|
58
|
+
className={cn(
|
|
59
|
+
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
|
|
60
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
61
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
62
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
data-slot="avatar-group"
|
|
74
|
+
className={cn(
|
|
75
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function AvatarGroupCount({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<"div">) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
data-slot="avatar-group-count"
|
|
90
|
+
className={cn(
|
|
91
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
|
92
|
+
className
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
Avatar,
|
|
101
|
+
AvatarImage,
|
|
102
|
+
AvatarFallback,
|
|
103
|
+
AvatarGroup,
|
|
104
|
+
AvatarGroupCount,
|
|
105
|
+
AvatarBadge,
|
|
106
|
+
}
|