@allbluecn/ui 0.2.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.
Files changed (49) hide show
  1. package/package.json +85 -0
  2. package/src/ai-command-button.tsx +36 -0
  3. package/src/ai-stream-text.tsx +37 -0
  4. package/src/alert-dialog.tsx +216 -0
  5. package/src/animated-theme-toggler.tsx +272 -0
  6. package/src/auth-client.ts +108 -0
  7. package/src/auth-layout.tsx +66 -0
  8. package/src/avatar.tsx +129 -0
  9. package/src/badge.tsx +66 -0
  10. package/src/button.tsx +84 -0
  11. package/src/card.tsx +120 -0
  12. package/src/checkbox.tsx +50 -0
  13. package/src/context-menu.tsx +280 -0
  14. package/src/dialog.tsx +183 -0
  15. package/src/dropdown-menu.tsx +286 -0
  16. package/src/empty.tsx +121 -0
  17. package/src/grid-pattern.tsx +87 -0
  18. package/src/icon-packs/index.ts +27 -0
  19. package/src/icon-packs/knowledge.ts +55 -0
  20. package/src/icon-packs/prd.ts +63 -0
  21. package/src/icon-packs/project.ts +59 -0
  22. package/src/icon-picker.tsx +68 -0
  23. package/src/index.ts +23 -0
  24. package/src/input.tsx +36 -0
  25. package/src/item.tsx +213 -0
  26. package/src/kbd.tsx +33 -0
  27. package/src/label.tsx +39 -0
  28. package/src/layout-panel-top.tsx +160 -0
  29. package/src/name-confirm-dialog.tsx +153 -0
  30. package/src/nav-float-button.tsx +301 -0
  31. package/src/notion-avatar.tsx +126 -0
  32. package/src/placeholder-page.tsx +42 -0
  33. package/src/popover.tsx +106 -0
  34. package/src/reference-inline.tsx +53 -0
  35. package/src/reference-search.tsx +52 -0
  36. package/src/resizable.tsx +67 -0
  37. package/src/select.tsx +207 -0
  38. package/src/separator.tsx +45 -0
  39. package/src/sheet.tsx +164 -0
  40. package/src/sidebar.tsx +708 -0
  41. package/src/skeleton.tsx +30 -0
  42. package/src/sonner.tsx +37 -0
  43. package/src/switch.tsx +48 -0
  44. package/src/textarea.tsx +35 -0
  45. package/src/timezone-provider.tsx +55 -0
  46. package/src/tooltip.tsx +72 -0
  47. package/src/use-mobile.ts +36 -0
  48. package/src/user-avatar.tsx +79 -0
  49. package/src/utils.ts +6 -0
@@ -0,0 +1,106 @@
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 { Popover as PopoverPrimitive } from "radix-ui"
22
+
23
+ import { cn } from "./utils"
24
+
25
+ function Popover({
26
+ ...props
27
+ }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
28
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />
29
+ }
30
+
31
+ function PopoverTrigger({
32
+ ...props
33
+ }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
34
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
35
+ }
36
+
37
+ function PopoverContent({
38
+ className,
39
+ align = "center",
40
+ sideOffset = 4,
41
+ ...props
42
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
43
+ return (
44
+ <PopoverPrimitive.Portal>
45
+ <PopoverPrimitive.Content
46
+ data-slot="popover-content"
47
+ align={align}
48
+ sideOffset={sideOffset}
49
+ className={cn(
50
+ "z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded-lg dark:border dark:border-primary/15 bg-popover p-2.5 text-sm text-popover-foreground shadow-md outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ </PopoverPrimitive.Portal>
56
+ )
57
+ }
58
+
59
+ function PopoverAnchor({
60
+ ...props
61
+ }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
62
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
63
+ }
64
+
65
+ function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="popover-header"
69
+ className={cn("flex flex-col gap-0.5 text-sm", className)}
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
76
+ return (
77
+ <div
78
+ data-slot="popover-title"
79
+ className={cn("font-medium", className)}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function PopoverDescription({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<"p">) {
89
+ return (
90
+ <p
91
+ data-slot="popover-description"
92
+ className={cn("text-muted-foreground", className)}
93
+ {...props}
94
+ />
95
+ )
96
+ }
97
+
98
+ export {
99
+ Popover,
100
+ PopoverAnchor,
101
+ PopoverContent,
102
+ PopoverDescription,
103
+ PopoverHeader,
104
+ PopoverTitle,
105
+ PopoverTrigger,
106
+ }
@@ -0,0 +1,53 @@
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 { useState, useEffect } from "react"
21
+ import type { RefDisplay, EntityUpdateEvent } from "@allbluecn/shared"
22
+
23
+ interface ReferenceInlineProps {
24
+ display: RefDisplay
25
+ entityId?: string
26
+ updateEvent?: EntityUpdateEvent | null
27
+ }
28
+
29
+ export function ReferenceInline({ display, entityId, updateEvent }: ReferenceInlineProps) {
30
+ const [currentDisplay, setCurrentDisplay] = useState(display)
31
+
32
+ useEffect(() => {
33
+ if (updateEvent && entityId && updateEvent.id === entityId) {
34
+ setCurrentDisplay(prev => ({ ...prev, ...updateEvent.fields as Partial<RefDisplay> }))
35
+ }
36
+ }, [updateEvent, entityId])
37
+
38
+ useEffect(() => {
39
+ setCurrentDisplay(display)
40
+ }, [display])
41
+
42
+ return (
43
+ <span className="inline-flex items-center gap-1.5 px-1.5 py-0.5 rounded bg-muted text-sm">
44
+ <span className="font-medium">{currentDisplay.title}</span>
45
+ {currentDisplay.status && (
46
+ <span className="text-xs text-muted-foreground">{currentDisplay.status}</span>
47
+ )}
48
+ {currentDisplay.priority && (
49
+ <span className="text-xs text-muted-foreground">{currentDisplay.priority}</span>
50
+ )}
51
+ </span>
52
+ )
53
+ }
@@ -0,0 +1,52 @@
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 type { SearchResult } from "@allbluecn/shared";
19
+
20
+ interface Props {
21
+ open: boolean;
22
+ query: string;
23
+ results: SearchResult[];
24
+ onSelect: (result: SearchResult) => void;
25
+ onClose: () => void;
26
+ }
27
+
28
+ export function ReferenceSearch({ open, query, results, onSelect, onClose }: Props) {
29
+ if (!open) return null;
30
+ return (
31
+ <div className="absolute z-50 w-80 rounded-md border bg-popover shadow-md">
32
+ <div className="p-2 border-b">
33
+ <input
34
+ className="w-full bg-transparent text-sm outline-none"
35
+ placeholder="搜索需求、Bug、原型..."
36
+ defaultValue={query}
37
+ autoFocus
38
+ />
39
+ </div>
40
+ {results.map((r) => (
41
+ <button
42
+ key={r.id}
43
+ className="w-full px-3 py-2 text-left text-sm hover:bg-accent"
44
+ onClick={() => onSelect(r)}
45
+ >
46
+ <span className="text-xs text-muted-foreground">{r.module}/</span>
47
+ {r.title}
48
+ </button>
49
+ ))}
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,67 @@
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 ResizablePrimitive from "react-resizable-panels"
21
+
22
+ import { cn } from "./utils"
23
+
24
+ function ResizablePanelGroup({
25
+ className,
26
+ ...props
27
+ }: ResizablePrimitive.GroupProps) {
28
+ return (
29
+ <ResizablePrimitive.Group
30
+ data-slot="resizable-panel-group"
31
+ className={cn(
32
+ "flex h-full w-full aria-[orientation=vertical]:flex-col",
33
+ className
34
+ )}
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
41
+ return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
42
+ }
43
+
44
+ function ResizableHandle({
45
+ withHandle,
46
+ className,
47
+ ...props
48
+ }: ResizablePrimitive.SeparatorProps & {
49
+ withHandle?: boolean
50
+ }) {
51
+ return (
52
+ <ResizablePrimitive.Separator
53
+ data-slot="resizable-handle"
54
+ className={cn(
55
+ "relative flex w-px items-center justify-center bg-border ring-offset-background after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
56
+ className
57
+ )}
58
+ {...props}
59
+ >
60
+ {withHandle && (
61
+ <div className="z-10 flex h-6 w-1 shrink-0 rounded-full bg-muted-foreground/25" />
62
+ )}
63
+ </ResizablePrimitive.Separator>
64
+ )
65
+ }
66
+
67
+ export { ResizableHandle, ResizablePanel, ResizablePanelGroup }
package/src/select.tsx ADDED
@@ -0,0 +1,207 @@
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 { Select as SelectPrimitive } from "radix-ui"
20
+
21
+ import { cn } from "./utils"
22
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
23
+
24
+ function Select({
25
+ ...props
26
+ }: React.ComponentProps<typeof SelectPrimitive.Root>) {
27
+ return <SelectPrimitive.Root data-slot="select" {...props} />
28
+ }
29
+
30
+ function SelectGroup({
31
+ className,
32
+ ...props
33
+ }: React.ComponentProps<typeof SelectPrimitive.Group>) {
34
+ return (
35
+ <SelectPrimitive.Group
36
+ data-slot="select-group"
37
+ className={cn("scroll-my-1 p-1", className)}
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function SelectValue({
44
+ ...props
45
+ }: React.ComponentProps<typeof SelectPrimitive.Value>) {
46
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />
47
+ }
48
+
49
+ function SelectTrigger({
50
+ className,
51
+ size = "default",
52
+ children,
53
+ ...props
54
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
55
+ size?: "sm" | "default"
56
+ }) {
57
+ return (
58
+ <SelectPrimitive.Trigger
59
+ data-slot="select-trigger"
60
+ data-size={size}
61
+ className={cn(
62
+ "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none 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 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
63
+ className
64
+ )}
65
+ {...props}
66
+ >
67
+ {children}
68
+ <SelectPrimitive.Icon asChild>
69
+ <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
70
+ </SelectPrimitive.Icon>
71
+ </SelectPrimitive.Trigger>
72
+ )
73
+ }
74
+
75
+ function SelectContent({
76
+ className,
77
+ children,
78
+ position = "item-aligned",
79
+ align = "center",
80
+ ...props
81
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
82
+ return (
83
+ <SelectPrimitive.Portal>
84
+ <SelectPrimitive.Content
85
+ data-slot="select-content"
86
+ data-align-trigger={position === "item-aligned"}
87
+ className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
88
+ position={position}
89
+ align={align}
90
+ {...props}
91
+ >
92
+ <SelectScrollUpButton />
93
+ <SelectPrimitive.Viewport
94
+ data-position={position}
95
+ className={cn(
96
+ "data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
97
+ position === "popper" && ""
98
+ )}
99
+ >
100
+ {children}
101
+ </SelectPrimitive.Viewport>
102
+ <SelectScrollDownButton />
103
+ </SelectPrimitive.Content>
104
+ </SelectPrimitive.Portal>
105
+ )
106
+ }
107
+
108
+ function SelectLabel({
109
+ className,
110
+ ...props
111
+ }: React.ComponentProps<typeof SelectPrimitive.Label>) {
112
+ return (
113
+ <SelectPrimitive.Label
114
+ data-slot="select-label"
115
+ className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
116
+ {...props}
117
+ />
118
+ )
119
+ }
120
+
121
+ function SelectItem({
122
+ className,
123
+ children,
124
+ ...props
125
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
126
+ return (
127
+ <SelectPrimitive.Item
128
+ data-slot="select-item"
129
+ className={cn(
130
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:*:[svg]:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
131
+ className
132
+ )}
133
+ {...props}
134
+ >
135
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
136
+ <SelectPrimitive.ItemIndicator>
137
+ <CheckIcon className="pointer-events-none" />
138
+ </SelectPrimitive.ItemIndicator>
139
+ </span>
140
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
141
+ </SelectPrimitive.Item>
142
+ )
143
+ }
144
+
145
+ function SelectSeparator({
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
149
+ return (
150
+ <SelectPrimitive.Separator
151
+ data-slot="select-separator"
152
+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ function SelectScrollUpButton({
159
+ className,
160
+ ...props
161
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
162
+ return (
163
+ <SelectPrimitive.ScrollUpButton
164
+ data-slot="select-scroll-up-button"
165
+ className={cn(
166
+ "z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
167
+ className
168
+ )}
169
+ {...props}
170
+ >
171
+ <ChevronUpIcon
172
+ />
173
+ </SelectPrimitive.ScrollUpButton>
174
+ )
175
+ }
176
+
177
+ function SelectScrollDownButton({
178
+ className,
179
+ ...props
180
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
181
+ return (
182
+ <SelectPrimitive.ScrollDownButton
183
+ data-slot="select-scroll-down-button"
184
+ className={cn(
185
+ "z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
186
+ className
187
+ )}
188
+ {...props}
189
+ >
190
+ <ChevronDownIcon
191
+ />
192
+ </SelectPrimitive.ScrollDownButton>
193
+ )
194
+ }
195
+
196
+ export {
197
+ Select,
198
+ SelectContent,
199
+ SelectGroup,
200
+ SelectItem,
201
+ SelectLabel,
202
+ SelectScrollDownButton,
203
+ SelectScrollUpButton,
204
+ SelectSeparator,
205
+ SelectTrigger,
206
+ SelectValue,
207
+ }
@@ -0,0 +1,45 @@
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 { Separator as SeparatorPrimitive } from "radix-ui"
22
+
23
+ import { cn } from "./utils"
24
+
25
+ function Separator({
26
+ className,
27
+ orientation = "horizontal",
28
+ decorative = true,
29
+ ...props
30
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
31
+ return (
32
+ <SeparatorPrimitive.Root
33
+ data-slot="separator"
34
+ decorative={decorative}
35
+ orientation={orientation}
36
+ className={cn(
37
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export { Separator }
package/src/sheet.tsx ADDED
@@ -0,0 +1,164 @@
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 { Dialog as SheetPrimitive } from "radix-ui"
22
+
23
+ import { cn } from "./utils"
24
+ import { Button } from "./button"
25
+ import { XIcon } from "lucide-react"
26
+
27
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
28
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
29
+ }
30
+
31
+ function SheetTrigger({
32
+ ...props
33
+ }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
34
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
35
+ }
36
+
37
+ function SheetClose({
38
+ ...props
39
+ }: React.ComponentProps<typeof SheetPrimitive.Close>) {
40
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
41
+ }
42
+
43
+ function SheetPortal({
44
+ ...props
45
+ }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
46
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
47
+ }
48
+
49
+ function SheetOverlay({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
53
+ return (
54
+ <SheetPrimitive.Overlay
55
+ data-slot="sheet-overlay"
56
+ className={cn(
57
+ "fixed inset-0 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",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function SheetContent({
66
+ className,
67
+ children,
68
+ side = "right",
69
+ showCloseButton = true,
70
+ ...props
71
+ }: React.ComponentProps<typeof SheetPrimitive.Content> & {
72
+ side?: "top" | "right" | "bottom" | "left"
73
+ showCloseButton?: boolean
74
+ }) {
75
+ return (
76
+ <SheetPortal>
77
+ <SheetOverlay />
78
+ <SheetPrimitive.Content
79
+ data-slot="sheet-content"
80
+ data-side={side}
81
+ className={cn(
82
+ "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10",
83
+ className
84
+ )}
85
+ {...props}
86
+ >
87
+ {children}
88
+ {showCloseButton && (
89
+ <SheetPrimitive.Close data-slot="sheet-close" asChild>
90
+ <Button
91
+ variant="ghost"
92
+ className="absolute top-3 right-3"
93
+ size="icon-sm"
94
+ >
95
+ <XIcon
96
+ />
97
+ <span className="sr-only">Close</span>
98
+ </Button>
99
+ </SheetPrimitive.Close>
100
+ )}
101
+ </SheetPrimitive.Content>
102
+ </SheetPortal>
103
+ )
104
+ }
105
+
106
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
107
+ return (
108
+ <div
109
+ data-slot="sheet-header"
110
+ className={cn("flex flex-col gap-0.5 p-4", className)}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
117
+ return (
118
+ <div
119
+ data-slot="sheet-footer"
120
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
121
+ {...props}
122
+ />
123
+ )
124
+ }
125
+
126
+ function SheetTitle({
127
+ className,
128
+ ...props
129
+ }: React.ComponentProps<typeof SheetPrimitive.Title>) {
130
+ return (
131
+ <SheetPrimitive.Title
132
+ data-slot="sheet-title"
133
+ className={cn(
134
+ "text-base font-medium text-foreground",
135
+ className
136
+ )}
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+
142
+ function SheetDescription({
143
+ className,
144
+ ...props
145
+ }: React.ComponentProps<typeof SheetPrimitive.Description>) {
146
+ return (
147
+ <SheetPrimitive.Description
148
+ data-slot="sheet-description"
149
+ className={cn("text-sm text-muted-foreground", className)}
150
+ {...props}
151
+ />
152
+ )
153
+ }
154
+
155
+ export {
156
+ Sheet,
157
+ SheetTrigger,
158
+ SheetClose,
159
+ SheetContent,
160
+ SheetHeader,
161
+ SheetFooter,
162
+ SheetTitle,
163
+ SheetDescription,
164
+ }