@srcroot/ui 0.0.1 → 0.0.3
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/dist/index.js +91 -0
- package/package.json +9 -3
- package/registry/badge.tsx +9 -25
- package/registry/breadcrumb.tsx +1 -1
- package/registry/button-group.tsx +9 -29
- package/registry/button.tsx +20 -46
- package/registry/calendar.tsx +416 -142
- package/registry/card.tsx +21 -47
- package/registry/combobox.tsx +171 -0
- package/registry/command.tsx +300 -0
- package/registry/container.tsx +9 -25
- package/registry/context-menu.tsx +221 -0
- package/registry/date-picker.tsx +179 -0
- package/registry/drawer.tsx +241 -0
- package/registry/dropdown-menu.tsx +93 -74
- package/registry/file-upload.tsx +240 -0
- package/registry/hover-card.tsx +165 -0
- package/registry/image.tsx +2 -2
- package/registry/kbd.tsx +60 -0
- package/registry/menubar.tsx +246 -0
- package/registry/native-select.tsx +49 -0
- package/registry/pagination.tsx +3 -0
- package/registry/resizable.tsx +251 -0
- package/registry/scroll-area.tsx +119 -0
- package/registry/search.tsx +2 -1
- package/registry/sheet.tsx +63 -18
- package/registry/sidebar.tsx +512 -0
- package/registry/slider.tsx +133 -54
- package/registry/text.tsx +7 -16
- package/registry/toggle-group.tsx +129 -0
- package/registry/toggle.tsx +72 -0
- package/registry/tooltip.tsx +21 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
interface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Orientation of scrollbar */
|
|
6
|
+
orientation?: "vertical" | "horizontal"
|
|
7
|
+
/** Scrollbar size: "thin" (4px), "default" (8px), "thick" (12px) */
|
|
8
|
+
scrollbarSize?: "thin" | "default" | "thick"
|
|
9
|
+
/** Hide scrollbar until hover */
|
|
10
|
+
hideScrollbar?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// CSS for custom scrollbar styling
|
|
14
|
+
const scrollbarStyles = {
|
|
15
|
+
thin: {
|
|
16
|
+
width: "4px",
|
|
17
|
+
height: "4px",
|
|
18
|
+
},
|
|
19
|
+
default: {
|
|
20
|
+
width: "8px",
|
|
21
|
+
height: "8px",
|
|
22
|
+
},
|
|
23
|
+
thick: {
|
|
24
|
+
width: "12px",
|
|
25
|
+
height: "12px",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ScrollArea - Custom scrollbar container
|
|
31
|
+
*
|
|
32
|
+
* Provides a styled scrollbar that is thin and consistent across browsers.
|
|
33
|
+
* Supports customizable scrollbar size and orientation.
|
|
34
|
+
*/
|
|
35
|
+
const ScrollArea = React.forwardRef<HTMLDivElement, ScrollAreaProps>(
|
|
36
|
+
({ className, children, orientation = "vertical", scrollbarSize = "thin", hideScrollbar = false, style, ...props }, ref) => {
|
|
37
|
+
const sizes = scrollbarStyles[scrollbarSize]
|
|
38
|
+
|
|
39
|
+
const scrollbarCSS: React.CSSProperties = {
|
|
40
|
+
...style,
|
|
41
|
+
// Webkit browsers (Chrome, Safari, Edge)
|
|
42
|
+
// @ts-ignore - CSS custom properties for scrollbar
|
|
43
|
+
"--scrollbar-width": sizes.width,
|
|
44
|
+
"--scrollbar-height": sizes.height,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn(
|
|
51
|
+
"relative",
|
|
52
|
+
// Container overflow based on orientation
|
|
53
|
+
orientation === "vertical" && "overflow-y-auto overflow-x-hidden",
|
|
54
|
+
orientation === "horizontal" && "overflow-x-auto overflow-y-hidden",
|
|
55
|
+
// Custom scrollbar classes
|
|
56
|
+
"scrollbar-custom",
|
|
57
|
+
hideScrollbar && "scrollbar-hide hover:scrollbar-show",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
style={scrollbarCSS}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
<style>{`
|
|
64
|
+
.scrollbar-custom::-webkit-scrollbar {
|
|
65
|
+
width: var(--scrollbar-width, 4px);
|
|
66
|
+
height: var(--scrollbar-height, 4px);
|
|
67
|
+
}
|
|
68
|
+
.scrollbar-custom::-webkit-scrollbar-track {
|
|
69
|
+
background: transparent;
|
|
70
|
+
border-radius: 9999px;
|
|
71
|
+
}
|
|
72
|
+
.scrollbar-custom::-webkit-scrollbar-thumb {
|
|
73
|
+
background: hsl(var(--muted-foreground) / 0.3);
|
|
74
|
+
border-radius: 9999px;
|
|
75
|
+
}
|
|
76
|
+
.scrollbar-custom::-webkit-scrollbar-thumb:hover {
|
|
77
|
+
background: hsl(var(--muted-foreground) / 0.5);
|
|
78
|
+
}
|
|
79
|
+
.scrollbar-custom {
|
|
80
|
+
scrollbar-width: thin;
|
|
81
|
+
scrollbar-color: hsl(var(--muted-foreground) / 0.3) transparent;
|
|
82
|
+
}
|
|
83
|
+
.scrollbar-hide::-webkit-scrollbar {
|
|
84
|
+
opacity: 0;
|
|
85
|
+
}
|
|
86
|
+
.scrollbar-hide:hover::-webkit-scrollbar,
|
|
87
|
+
.scrollbar-show::-webkit-scrollbar {
|
|
88
|
+
opacity: 1;
|
|
89
|
+
}
|
|
90
|
+
`}</style>
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
ScrollArea.displayName = "ScrollArea"
|
|
97
|
+
|
|
98
|
+
// ScrollBar component for explicit scrollbar styling reference (optional usage)
|
|
99
|
+
interface ScrollBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
100
|
+
orientation?: "vertical" | "horizontal"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const ScrollBar = React.forwardRef<HTMLDivElement, ScrollBarProps>(
|
|
104
|
+
({ className, orientation = "vertical", ...props }, ref) => (
|
|
105
|
+
<div
|
|
106
|
+
ref={ref}
|
|
107
|
+
className={cn(
|
|
108
|
+
"flex touch-none select-none transition-colors",
|
|
109
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
110
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
ScrollBar.displayName = "ScrollBar"
|
|
118
|
+
|
|
119
|
+
export { ScrollArea, ScrollBar }
|
package/registry/search.tsx
CHANGED
|
@@ -97,7 +97,8 @@ const Search = React.forwardRef<HTMLInputElement, SearchProps>(
|
|
|
97
97
|
"flex h-10 w-full rounded-md border border-input bg-transparent pl-10 pr-10 py-2 text-sm shadow-sm transition-colors",
|
|
98
98
|
"placeholder:text-muted-foreground",
|
|
99
99
|
"focus:outline-none focus:ring-1 focus:ring-ring",
|
|
100
|
-
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
100
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
101
|
+
"[&::-webkit-search-cancel-button]:appearance-none"
|
|
101
102
|
)}
|
|
102
103
|
{...props}
|
|
103
104
|
/>
|
package/registry/sheet.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client'
|
|
1
2
|
import * as React from "react"
|
|
2
3
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
4
|
import { cn } from "@/lib/utils"
|
|
@@ -17,20 +18,7 @@ interface SheetProps {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
* Sheet (slide-in panel) component
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* <Sheet>
|
|
24
|
-
* <SheetTrigger asChild>
|
|
25
|
-
* <Button>Open Sheet</Button>
|
|
26
|
-
* </SheetTrigger>
|
|
27
|
-
* <SheetContent side="right">
|
|
28
|
-
* <SheetHeader>
|
|
29
|
-
* <SheetTitle>Title</SheetTitle>
|
|
30
|
-
* <SheetDescription>Description</SheetDescription>
|
|
31
|
-
* </SheetHeader>
|
|
32
|
-
* </SheetContent>
|
|
33
|
-
* </Sheet>
|
|
21
|
+
* Sheet (slide-in panel) component with smooth animations
|
|
34
22
|
*/
|
|
35
23
|
function Sheet({ children, open: controlledOpen, onOpenChange, defaultOpen = false }: SheetProps) {
|
|
36
24
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
|
|
@@ -76,7 +64,7 @@ const SheetTrigger = React.forwardRef<HTMLButtonElement, SheetTriggerProps>(
|
|
|
76
64
|
SheetTrigger.displayName = "SheetTrigger"
|
|
77
65
|
|
|
78
66
|
const sheetVariants = cva(
|
|
79
|
-
"fixed z-50 gap-4 bg-background p-6 shadow-lg
|
|
67
|
+
"fixed z-50 gap-4 bg-background p-6 shadow-lg",
|
|
80
68
|
{
|
|
81
69
|
variants: {
|
|
82
70
|
side: {
|
|
@@ -92,6 +80,26 @@ const sheetVariants = cva(
|
|
|
92
80
|
}
|
|
93
81
|
)
|
|
94
82
|
|
|
83
|
+
// Animation classes for each side
|
|
84
|
+
const animationClasses = {
|
|
85
|
+
top: {
|
|
86
|
+
open: "translate-y-0",
|
|
87
|
+
closed: "-translate-y-full",
|
|
88
|
+
},
|
|
89
|
+
bottom: {
|
|
90
|
+
open: "translate-y-0",
|
|
91
|
+
closed: "translate-y-full",
|
|
92
|
+
},
|
|
93
|
+
left: {
|
|
94
|
+
open: "translate-x-0",
|
|
95
|
+
closed: "-translate-x-full",
|
|
96
|
+
},
|
|
97
|
+
right: {
|
|
98
|
+
open: "translate-x-0",
|
|
99
|
+
closed: "translate-x-full",
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
interface SheetContentProps
|
|
96
104
|
extends React.HTMLAttributes<HTMLDivElement>,
|
|
97
105
|
VariantProps<typeof sheetVariants> { }
|
|
@@ -101,6 +109,29 @@ const SheetContent = React.forwardRef<HTMLDivElement, SheetContentProps>(
|
|
|
101
109
|
const context = React.useContext(SheetContext)
|
|
102
110
|
if (!context) throw new Error("SheetContent must be used within Sheet")
|
|
103
111
|
|
|
112
|
+
const [isVisible, setIsVisible] = React.useState(false)
|
|
113
|
+
const [isAnimating, setIsAnimating] = React.useState(false)
|
|
114
|
+
|
|
115
|
+
React.useEffect(() => {
|
|
116
|
+
if (context.open) {
|
|
117
|
+
// First make visible (off-screen)
|
|
118
|
+
setIsVisible(true)
|
|
119
|
+
// Use a small timeout to ensure the browser has painted the initial state
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
setIsAnimating(true)
|
|
122
|
+
}, 10)
|
|
123
|
+
return () => clearTimeout(timer)
|
|
124
|
+
} else {
|
|
125
|
+
// Start close animation
|
|
126
|
+
setIsAnimating(false)
|
|
127
|
+
// Wait for animation to complete before hiding
|
|
128
|
+
const timer = setTimeout(() => {
|
|
129
|
+
setIsVisible(false)
|
|
130
|
+
}, 300)
|
|
131
|
+
return () => clearTimeout(timer)
|
|
132
|
+
}
|
|
133
|
+
}, [context.open])
|
|
134
|
+
|
|
104
135
|
React.useEffect(() => {
|
|
105
136
|
const handleEscape = (e: KeyboardEvent) => {
|
|
106
137
|
if (e.key === "Escape") {
|
|
@@ -119,19 +150,33 @@ const SheetContent = React.forwardRef<HTMLDivElement, SheetContentProps>(
|
|
|
119
150
|
}
|
|
120
151
|
}, [context.open, context])
|
|
121
152
|
|
|
122
|
-
if (!
|
|
153
|
+
if (!isVisible) return null
|
|
154
|
+
|
|
155
|
+
const sideKey = side || "right"
|
|
123
156
|
|
|
124
157
|
return (
|
|
125
158
|
<>
|
|
159
|
+
{/* Overlay with fade animation */}
|
|
126
160
|
<div
|
|
127
|
-
className=
|
|
161
|
+
className={cn(
|
|
162
|
+
"fixed inset-0 z-50 bg-black/80 transition-opacity duration-300",
|
|
163
|
+
isAnimating ? "opacity-100" : "opacity-0"
|
|
164
|
+
)}
|
|
128
165
|
onClick={() => context.onOpenChange(false)}
|
|
129
166
|
/>
|
|
167
|
+
{/* Sheet content with slide animation */}
|
|
130
168
|
<div
|
|
131
169
|
ref={ref}
|
|
132
170
|
role="dialog"
|
|
133
171
|
aria-modal="true"
|
|
134
|
-
className={cn(
|
|
172
|
+
className={cn(
|
|
173
|
+
sheetVariants({ side }),
|
|
174
|
+
"transition-transform duration-300 ease-out",
|
|
175
|
+
isAnimating
|
|
176
|
+
? animationClasses[sideKey].open
|
|
177
|
+
: animationClasses[sideKey].closed,
|
|
178
|
+
className
|
|
179
|
+
)}
|
|
135
180
|
{...props}
|
|
136
181
|
>
|
|
137
182
|
{children}
|