@allbluecn/admin-app 0.3.3 → 0.3.4
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/allblue-ui/package.json +6 -0
- package/allblue-ui/src/components/confirm-dialog.tsx +94 -0
- package/allblue-ui/src/components/infinite-scroll-sentinel.tsx +37 -0
- package/allblue-ui/src/components/notion-avatar.tsx +126 -0
- package/allblue-ui/src/components/user-avatar.tsx +82 -0
- package/allblue-ui/src/data-table/data-table-pagination.tsx +121 -0
- package/allblue-ui/src/data-table/data-table.tsx +218 -0
- package/allblue-ui/src/data-table/table-detail-layout.tsx +97 -0
- package/allblue-ui/src/hooks/use-load-more.ts +57 -0
- package/allblue-ui/src/hooks/use-mobile.ts +19 -0
- package/allblue-ui/src/index.ts +31 -0
- package/allblue-ui/src/lib/avatar.ts +41 -0
- package/allblue-ui/src/lib/datetime.ts +226 -0
- package/allblue-ui/src/lib/sanitize.ts +18 -0
- package/allblue-ui/src/lib/utils.ts +1 -0
- package/allblue-ui/src/ui/alert-dialog.tsx +185 -0
- package/allblue-ui/src/ui/alert.tsx +75 -0
- package/allblue-ui/src/ui/avatar.tsx +106 -0
- package/allblue-ui/src/ui/badge.tsx +51 -0
- package/allblue-ui/src/ui/button.tsx +57 -0
- package/allblue-ui/src/ui/card.tsx +102 -0
- package/allblue-ui/src/ui/checkbox.tsx +27 -0
- package/allblue-ui/src/ui/collapsible.tsx +19 -0
- package/allblue-ui/src/ui/dialog.tsx +155 -0
- package/allblue-ui/src/ui/dropdown-menu.tsx +265 -0
- package/allblue-ui/src/ui/field.tsx +236 -0
- package/allblue-ui/src/ui/input-group.tsx +157 -0
- package/allblue-ui/src/ui/input.tsx +19 -0
- package/allblue-ui/src/ui/label.tsx +17 -0
- package/allblue-ui/src/ui/pagination.tsx +130 -0
- package/allblue-ui/src/ui/select.tsx +232 -0
- package/allblue-ui/src/ui/separator.tsx +23 -0
- package/allblue-ui/src/ui/sheet.tsx +134 -0
- package/allblue-ui/src/ui/sidebar.tsx +721 -0
- package/allblue-ui/src/ui/skeleton.tsx +13 -0
- package/allblue-ui/src/ui/switch.tsx +30 -0
- package/allblue-ui/src/ui/table.tsx +113 -0
- package/allblue-ui/src/ui/textarea.tsx +17 -0
- package/allblue-ui/src/ui/toast.tsx +308 -0
- package/allblue-ui/src/ui/tooltip.tsx +64 -0
- package/package.json +3 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "cn"
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn("animate-pulse rounded-md bg-muted", className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { Skeleton }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
function Switch({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: SwitchPrimitive.Root.Props & {
|
|
10
|
+
size?: "sm" | "default"
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<SwitchPrimitive.Root
|
|
14
|
+
data-slot="switch"
|
|
15
|
+
data-size={size}
|
|
16
|
+
className={cn(
|
|
17
|
+
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
22
|
+
<SwitchPrimitive.Thumb
|
|
23
|
+
data-slot="switch-thumb"
|
|
24
|
+
className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
|
|
25
|
+
/>
|
|
26
|
+
</SwitchPrimitive.Root>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Switch }
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "cn"
|
|
3
|
+
|
|
4
|
+
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|
5
|
+
return (
|
|
6
|
+
<div
|
|
7
|
+
data-slot="table-container"
|
|
8
|
+
className="relative w-full overflow-x-auto"
|
|
9
|
+
>
|
|
10
|
+
<table
|
|
11
|
+
data-slot="table"
|
|
12
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
20
|
+
return (
|
|
21
|
+
<thead
|
|
22
|
+
data-slot="table-header"
|
|
23
|
+
className={cn("[&_tr]:border-b", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
30
|
+
return (
|
|
31
|
+
<tbody
|
|
32
|
+
data-slot="table-body"
|
|
33
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
40
|
+
return (
|
|
41
|
+
<tfoot
|
|
42
|
+
data-slot="table-footer"
|
|
43
|
+
className={cn(
|
|
44
|
+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
53
|
+
return (
|
|
54
|
+
<tr
|
|
55
|
+
data-slot="table-row"
|
|
56
|
+
className={cn(
|
|
57
|
+
"border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
66
|
+
return (
|
|
67
|
+
<th
|
|
68
|
+
data-slot="table-head"
|
|
69
|
+
className={cn(
|
|
70
|
+
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
79
|
+
return (
|
|
80
|
+
<td
|
|
81
|
+
data-slot="table-cell"
|
|
82
|
+
className={cn(
|
|
83
|
+
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function TableCaption({
|
|
92
|
+
className,
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentProps<"caption">) {
|
|
95
|
+
return (
|
|
96
|
+
<caption
|
|
97
|
+
data-slot="table-caption"
|
|
98
|
+
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
Table,
|
|
106
|
+
TableHeader,
|
|
107
|
+
TableBody,
|
|
108
|
+
TableFooter,
|
|
109
|
+
TableHead,
|
|
110
|
+
TableRow,
|
|
111
|
+
TableCell,
|
|
112
|
+
TableCaption,
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "cn"
|
|
3
|
+
|
|
4
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
5
|
+
return (
|
|
6
|
+
<textarea
|
|
7
|
+
data-slot="textarea"
|
|
8
|
+
className={cn(
|
|
9
|
+
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { Textarea }
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Toast as ToastPrimitive } from "@base-ui/react/toast"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
import { Button } from "./button"
|
|
6
|
+
import { XIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
|
|
7
|
+
|
|
8
|
+
const toastManager = ToastPrimitive.createToastManager()
|
|
9
|
+
|
|
10
|
+
// 双端历史分叉:web 顶部居中堆叠(top-center),admin 右下角堆叠(bottom-right)。
|
|
11
|
+
// 差异为纯 class 组 + swipeDirection,通过 position 参数承载,行为逐字节等价于原实现。
|
|
12
|
+
type ToasterPosition = "top-center" | "bottom-right"
|
|
13
|
+
|
|
14
|
+
const ToastPositionContext = React.createContext<ToasterPosition>("top-center")
|
|
15
|
+
|
|
16
|
+
interface SonnerToastOptions {
|
|
17
|
+
description?: React.ReactNode
|
|
18
|
+
duration?: number
|
|
19
|
+
action?: { label: React.ReactNode; onClick?: () => void }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildOptions(message: React.ReactNode, type: string | undefined, opts?: SonnerToastOptions) {
|
|
23
|
+
return {
|
|
24
|
+
type,
|
|
25
|
+
title: message,
|
|
26
|
+
description: opts?.description,
|
|
27
|
+
timeout: opts?.duration,
|
|
28
|
+
actionProps: opts?.action
|
|
29
|
+
? { children: opts.action.label, onClick: opts.action.onClick }
|
|
30
|
+
: undefined,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const toast = Object.assign(
|
|
35
|
+
(message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
36
|
+
toastManager.add(buildOptions(message, undefined, opts)),
|
|
37
|
+
{
|
|
38
|
+
success: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
39
|
+
toastManager.add(buildOptions(message, "success", opts)),
|
|
40
|
+
error: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
41
|
+
toastManager.add(buildOptions(message, "error", opts)),
|
|
42
|
+
warning: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
43
|
+
toastManager.add(buildOptions(message, "warning", opts)),
|
|
44
|
+
info: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
45
|
+
toastManager.add(buildOptions(message, "info", opts)),
|
|
46
|
+
loading: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
47
|
+
toastManager.add(buildOptions(message, "loading", { ...opts, duration: 0 })),
|
|
48
|
+
message: (message: React.ReactNode, opts?: SonnerToastOptions) =>
|
|
49
|
+
toastManager.add(buildOptions(message, undefined, opts)),
|
|
50
|
+
dismiss: (id?: string) => toastManager.close(id),
|
|
51
|
+
promise: <Value,>(
|
|
52
|
+
promiseValue: Promise<Value>,
|
|
53
|
+
options: {
|
|
54
|
+
loading: string
|
|
55
|
+
success: string | ((result: Value) => string)
|
|
56
|
+
error: string | ((error: unknown) => string)
|
|
57
|
+
},
|
|
58
|
+
) =>
|
|
59
|
+
toastManager.promise(promiseValue, {
|
|
60
|
+
loading: options.loading,
|
|
61
|
+
success: options.success,
|
|
62
|
+
error: options.error,
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
|
|
68
|
+
return <ToastPrimitive.Provider {...props} />
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
|
|
72
|
+
return <ToastPrimitive.Portal data-slot="toast-portal" {...props} />
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {
|
|
76
|
+
const top = React.useContext(ToastPositionContext) === "top-center"
|
|
77
|
+
return (
|
|
78
|
+
<ToastPrimitive.Viewport
|
|
79
|
+
data-slot="toast-viewport"
|
|
80
|
+
className={cn(
|
|
81
|
+
top
|
|
82
|
+
? "pointer-events-none fixed top-4 left-0 right-0 z-50 mx-auto w-full max-w-sm outline-none sm:top-6"
|
|
83
|
+
: "pointer-events-none fixed bottom-4 right-4 z-50 w-full max-w-sm outline-none sm:bottom-6 sm:right-6",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function Toast({ className, ...props }: ToastPrimitive.Root.Props) {
|
|
92
|
+
const top = React.useContext(ToastPositionContext) === "top-center"
|
|
93
|
+
return (
|
|
94
|
+
<ToastPrimitive.Root
|
|
95
|
+
data-slot="toast"
|
|
96
|
+
className={cn(
|
|
97
|
+
top
|
|
98
|
+
? "group/toast pointer-events-auto absolute top-0 right-0 left-0 z-[calc(1000-var(--toast-index))] mx-auto w-full origin-top rounded-2xl border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
|
99
|
+
: "group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-2xl border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
100
|
+
top
|
|
101
|
+
? "[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)+calc(var(--toast-index)*var(--gap))+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]"
|
|
102
|
+
: "[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]",
|
|
103
|
+
top
|
|
104
|
+
? "h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--peek))+(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]"
|
|
105
|
+
: "h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]",
|
|
106
|
+
top
|
|
107
|
+
? "after:absolute after:bottom-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']"
|
|
108
|
+
: "after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']",
|
|
109
|
+
"data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]",
|
|
110
|
+
top
|
|
111
|
+
? "data-limited:opacity-0 data-starting-style:[transform:translateY(-150%)]"
|
|
112
|
+
: "data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]",
|
|
113
|
+
top
|
|
114
|
+
? "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(-150%)]"
|
|
115
|
+
: "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]",
|
|
116
|
+
"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
117
|
+
"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
118
|
+
"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
119
|
+
"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
120
|
+
"data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
121
|
+
"data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
122
|
+
"data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
123
|
+
"data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
{...props}
|
|
127
|
+
/>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {
|
|
132
|
+
return (
|
|
133
|
+
<ToastPrimitive.Content
|
|
134
|
+
data-slot="toast-content"
|
|
135
|
+
className={cn(
|
|
136
|
+
"flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100",
|
|
137
|
+
className
|
|
138
|
+
)}
|
|
139
|
+
{...props}
|
|
140
|
+
/>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {
|
|
145
|
+
return (
|
|
146
|
+
<ToastPrimitive.Title
|
|
147
|
+
data-slot="toast-title"
|
|
148
|
+
className={cn("text-sm font-medium", className)}
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function ToastDescription({
|
|
155
|
+
className,
|
|
156
|
+
...props
|
|
157
|
+
}: ToastPrimitive.Description.Props) {
|
|
158
|
+
return (
|
|
159
|
+
<ToastPrimitive.Description
|
|
160
|
+
data-slot="toast-description"
|
|
161
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
162
|
+
{...props}
|
|
163
|
+
/>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function ToastAction({
|
|
168
|
+
className,
|
|
169
|
+
render = <Button variant="outline" size="sm" />,
|
|
170
|
+
...props
|
|
171
|
+
}: ToastPrimitive.Action.Props) {
|
|
172
|
+
return (
|
|
173
|
+
<ToastPrimitive.Action
|
|
174
|
+
data-slot="toast-action"
|
|
175
|
+
render={render}
|
|
176
|
+
className={cn("shrink-0", className)}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function ToastClose({
|
|
183
|
+
className,
|
|
184
|
+
children,
|
|
185
|
+
render = <Button variant="ghost" size="icon-sm" />,
|
|
186
|
+
...props
|
|
187
|
+
}: ToastPrimitive.Close.Props) {
|
|
188
|
+
return (
|
|
189
|
+
<ToastPrimitive.Close
|
|
190
|
+
data-slot="toast-close"
|
|
191
|
+
aria-label="Close toast"
|
|
192
|
+
render={render}
|
|
193
|
+
className={cn(
|
|
194
|
+
"relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground",
|
|
195
|
+
className
|
|
196
|
+
)}
|
|
197
|
+
{...props}
|
|
198
|
+
>
|
|
199
|
+
{children ?? (
|
|
200
|
+
<XIcon aria-hidden="true" />
|
|
201
|
+
)}
|
|
202
|
+
</ToastPrimitive.Close>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function ToastIcon({ type }: { type: string | undefined }) {
|
|
207
|
+
let icon: React.ReactNode = null
|
|
208
|
+
|
|
209
|
+
if (type === "success") {
|
|
210
|
+
icon = (
|
|
211
|
+
<CircleCheckIcon aria-hidden="true" />
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (type === "info") {
|
|
216
|
+
icon = (
|
|
217
|
+
<InfoIcon aria-hidden="true" />
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (type === "warning") {
|
|
222
|
+
icon = (
|
|
223
|
+
<TriangleAlertIcon aria-hidden="true" />
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (type === "error") {
|
|
228
|
+
icon = (
|
|
229
|
+
<OctagonXIcon className="text-destructive" aria-hidden="true" />
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (type === "loading") {
|
|
234
|
+
icon = (
|
|
235
|
+
<Loader2Icon className="animate-spin" aria-hidden="true" />
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!icon) {
|
|
240
|
+
return null
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<span
|
|
245
|
+
data-slot="toast-icon"
|
|
246
|
+
className="shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"
|
|
247
|
+
>
|
|
248
|
+
{icon}
|
|
249
|
+
</span>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function ToastList() {
|
|
254
|
+
const { toasts } = ToastPrimitive.useToastManager()
|
|
255
|
+
const top = React.useContext(ToastPositionContext) === "top-center"
|
|
256
|
+
|
|
257
|
+
return toasts.map((toastItem) => (
|
|
258
|
+
<Toast key={toastItem.id} toast={toastItem} swipeDirection={top ? "up" : undefined}>
|
|
259
|
+
<ToastContent>
|
|
260
|
+
<ToastIcon type={toastItem.type} />
|
|
261
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
262
|
+
<ToastTitle />
|
|
263
|
+
<ToastDescription />
|
|
264
|
+
</div>
|
|
265
|
+
<ToastAction />
|
|
266
|
+
<ToastClose />
|
|
267
|
+
</ToastContent>
|
|
268
|
+
</Toast>
|
|
269
|
+
))
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function Toaster({
|
|
273
|
+
children,
|
|
274
|
+
position = "top-center",
|
|
275
|
+
...props
|
|
276
|
+
}: ToastPrimitive.Provider.Props & { position?: ToasterPosition }) {
|
|
277
|
+
return (
|
|
278
|
+
<ToastPositionContext.Provider value={position}>
|
|
279
|
+
<ToastProvider toastManager={toastManager} {...props}>
|
|
280
|
+
{children}
|
|
281
|
+
<ToastPortal>
|
|
282
|
+
<ToastViewport>
|
|
283
|
+
<ToastList />
|
|
284
|
+
</ToastViewport>
|
|
285
|
+
</ToastPortal>
|
|
286
|
+
</ToastProvider>
|
|
287
|
+
</ToastPositionContext.Provider>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const createToastManager = ToastPrimitive.createToastManager
|
|
292
|
+
const useToastManager = ToastPrimitive.useToastManager
|
|
293
|
+
|
|
294
|
+
export {
|
|
295
|
+
Toaster,
|
|
296
|
+
Toast,
|
|
297
|
+
ToastAction,
|
|
298
|
+
ToastClose,
|
|
299
|
+
ToastContent,
|
|
300
|
+
ToastDescription,
|
|
301
|
+
ToastPortal,
|
|
302
|
+
ToastProvider,
|
|
303
|
+
ToastTitle,
|
|
304
|
+
ToastViewport,
|
|
305
|
+
createToastManager,
|
|
306
|
+
toast,
|
|
307
|
+
useToastManager,
|
|
308
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
function TooltipProvider({
|
|
6
|
+
delay = 0,
|
|
7
|
+
...props
|
|
8
|
+
}: TooltipPrimitive.Provider.Props) {
|
|
9
|
+
return (
|
|
10
|
+
<TooltipPrimitive.Provider
|
|
11
|
+
data-slot="tooltip-provider"
|
|
12
|
+
delay={delay}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
|
|
19
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
|
23
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function TooltipContent({
|
|
27
|
+
className,
|
|
28
|
+
side = "top",
|
|
29
|
+
sideOffset = 4,
|
|
30
|
+
align = "center",
|
|
31
|
+
alignOffset = 0,
|
|
32
|
+
children,
|
|
33
|
+
...props
|
|
34
|
+
}: TooltipPrimitive.Popup.Props &
|
|
35
|
+
Pick<
|
|
36
|
+
TooltipPrimitive.Positioner.Props,
|
|
37
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
38
|
+
>) {
|
|
39
|
+
return (
|
|
40
|
+
<TooltipPrimitive.Portal>
|
|
41
|
+
<TooltipPrimitive.Positioner
|
|
42
|
+
align={align}
|
|
43
|
+
alignOffset={alignOffset}
|
|
44
|
+
side={side}
|
|
45
|
+
sideOffset={sideOffset}
|
|
46
|
+
className="isolate z-50"
|
|
47
|
+
>
|
|
48
|
+
<TooltipPrimitive.Popup
|
|
49
|
+
data-slot="tooltip-content"
|
|
50
|
+
className={cn(
|
|
51
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 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",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
|
|
58
|
+
</TooltipPrimitive.Popup>
|
|
59
|
+
</TooltipPrimitive.Positioner>
|
|
60
|
+
</TooltipPrimitive.Portal>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/admin-app",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
},
|
|
81
81
|
"files": [
|
|
82
82
|
"src",
|
|
83
|
-
"public"
|
|
83
|
+
"public",
|
|
84
|
+
"allblue-ui"
|
|
84
85
|
],
|
|
85
86
|
"publishConfig": {
|
|
86
87
|
"access": "public"
|