@srcroot/ui 0.0.1 → 0.0.2
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/calendar.tsx +416 -142
- package/registry/combobox.tsx +174 -0
- package/registry/command.tsx +298 -0
- package/registry/context-menu.tsx +221 -0
- package/registry/date-picker.tsx +179 -0
- package/registry/drawer.tsx +217 -0
- package/registry/dropdown-menu.tsx +1 -30
- package/registry/file-upload.tsx +240 -0
- package/registry/hover-card.tsx +165 -0
- 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 +213 -0
- package/registry/scroll-area.tsx +60 -0
- package/registry/search.tsx +2 -1
- package/registry/sheet.tsx +1 -0
- package/registry/sidebar.tsx +505 -0
- package/registry/slider.tsx +82 -18
- package/registry/toggle-group.tsx +129 -0
- package/registry/toggle.tsx +72 -0
- package/registry/tooltip.tsx +21 -3
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
// Resizable Context
|
|
7
|
+
interface ResizablePanelGroupContextValue {
|
|
8
|
+
direction: "horizontal" | "vertical"
|
|
9
|
+
sizes: number[]
|
|
10
|
+
setSizes: React.Dispatch<React.SetStateAction<number[]>>
|
|
11
|
+
registerPanel: () => number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ResizablePanelGroupContext = React.createContext<ResizablePanelGroupContextValue | null>(null)
|
|
15
|
+
|
|
16
|
+
function useResizablePanelGroup() {
|
|
17
|
+
const context = React.useContext(ResizablePanelGroupContext)
|
|
18
|
+
if (!context) {
|
|
19
|
+
throw new Error("useResizablePanelGroup must be used within a ResizablePanelGroup")
|
|
20
|
+
}
|
|
21
|
+
return context
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ResizablePanelGroup
|
|
25
|
+
interface ResizablePanelGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
26
|
+
direction?: "horizontal" | "vertical"
|
|
27
|
+
onLayout?: (sizes: number[]) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ResizablePanelGroup = React.forwardRef<HTMLDivElement, ResizablePanelGroupProps>(
|
|
31
|
+
({ className, direction = "horizontal", children, onLayout, ...props }, ref) => {
|
|
32
|
+
const [sizes, setSizes] = React.useState<number[]>([])
|
|
33
|
+
const panelCountRef = React.useRef(0)
|
|
34
|
+
|
|
35
|
+
const registerPanel = React.useCallback(() => {
|
|
36
|
+
const index = panelCountRef.current
|
|
37
|
+
panelCountRef.current += 1
|
|
38
|
+
return index
|
|
39
|
+
}, [])
|
|
40
|
+
|
|
41
|
+
// Notify layout changes
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (sizes.length > 0) {
|
|
44
|
+
onLayout?.(sizes)
|
|
45
|
+
}
|
|
46
|
+
}, [sizes, onLayout])
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<ResizablePanelGroupContext.Provider value={{ direction, sizes, setSizes, registerPanel }}>
|
|
50
|
+
<div
|
|
51
|
+
ref={ref}
|
|
52
|
+
data-panel-group
|
|
53
|
+
data-direction={direction}
|
|
54
|
+
className={cn(
|
|
55
|
+
"flex h-full w-full",
|
|
56
|
+
direction === "horizontal" ? "flex-row" : "flex-col",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</div>
|
|
63
|
+
</ResizablePanelGroupContext.Provider>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
ResizablePanelGroup.displayName = "ResizablePanelGroup"
|
|
68
|
+
|
|
69
|
+
// ResizablePanel
|
|
70
|
+
interface ResizablePanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
71
|
+
defaultSize?: number
|
|
72
|
+
minSize?: number
|
|
73
|
+
maxSize?: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ResizablePanel = React.forwardRef<HTMLDivElement, ResizablePanelProps>(
|
|
77
|
+
({ className, defaultSize = 50, minSize = 10, maxSize = 90, children, style, ...props }, ref) => {
|
|
78
|
+
const { direction, sizes, setSizes, registerPanel } = useResizablePanelGroup()
|
|
79
|
+
const indexRef = React.useRef<number | null>(null)
|
|
80
|
+
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
if (indexRef.current === null) {
|
|
83
|
+
indexRef.current = registerPanel()
|
|
84
|
+
setSizes(prev => {
|
|
85
|
+
const newSizes = [...prev]
|
|
86
|
+
newSizes[indexRef.current!] = defaultSize
|
|
87
|
+
return newSizes
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}, [registerPanel, setSizes, defaultSize])
|
|
91
|
+
|
|
92
|
+
const size = indexRef.current !== null ? sizes[indexRef.current] ?? defaultSize : defaultSize
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
ref={ref}
|
|
97
|
+
data-panel
|
|
98
|
+
data-panel-index={indexRef.current}
|
|
99
|
+
className={cn("overflow-hidden", className)}
|
|
100
|
+
style={{
|
|
101
|
+
...style,
|
|
102
|
+
[direction === "horizontal" ? "width" : "height"]: `${size}%`,
|
|
103
|
+
flexShrink: 0,
|
|
104
|
+
flexGrow: 0,
|
|
105
|
+
}}
|
|
106
|
+
{...props}
|
|
107
|
+
>
|
|
108
|
+
{children}
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
ResizablePanel.displayName = "ResizablePanel"
|
|
114
|
+
|
|
115
|
+
// ResizableHandle
|
|
116
|
+
interface ResizableHandleProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
117
|
+
withHandle?: boolean
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const ResizableHandle = React.forwardRef<HTMLDivElement, ResizableHandleProps>(
|
|
121
|
+
({ className, withHandle = false, ...props }, ref) => {
|
|
122
|
+
const { direction, sizes, setSizes } = useResizablePanelGroup()
|
|
123
|
+
const [isDragging, setIsDragging] = React.useState(false)
|
|
124
|
+
const handleRef = React.useRef<HTMLDivElement>(null)
|
|
125
|
+
|
|
126
|
+
const handleMouseDown = (e: React.MouseEvent) => {
|
|
127
|
+
e.preventDefault()
|
|
128
|
+
setIsDragging(true)
|
|
129
|
+
|
|
130
|
+
const startX = e.clientX
|
|
131
|
+
const startY = e.clientY
|
|
132
|
+
const startSizes = [...sizes]
|
|
133
|
+
const container = handleRef.current?.parentElement
|
|
134
|
+
|
|
135
|
+
if (!container) return
|
|
136
|
+
|
|
137
|
+
const containerRect = container.getBoundingClientRect()
|
|
138
|
+
const containerSize = direction === "horizontal" ? containerRect.width : containerRect.height
|
|
139
|
+
|
|
140
|
+
const handleMouseMove = (moveEvent: MouseEvent) => {
|
|
141
|
+
const delta = direction === "horizontal"
|
|
142
|
+
? moveEvent.clientX - startX
|
|
143
|
+
: moveEvent.clientY - startY
|
|
144
|
+
const deltaPercent = (delta / containerSize) * 100
|
|
145
|
+
|
|
146
|
+
setSizes(prev => {
|
|
147
|
+
const newSizes = [...prev]
|
|
148
|
+
// Adjust first two panels (simple implementation)
|
|
149
|
+
if (newSizes.length >= 2) {
|
|
150
|
+
newSizes[0] = Math.max(10, Math.min(90, startSizes[0] + deltaPercent))
|
|
151
|
+
newSizes[1] = Math.max(10, Math.min(90, startSizes[1] - deltaPercent))
|
|
152
|
+
}
|
|
153
|
+
return newSizes
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const handleMouseUp = () => {
|
|
158
|
+
setIsDragging(false)
|
|
159
|
+
document.removeEventListener("mousemove", handleMouseMove)
|
|
160
|
+
document.removeEventListener("mouseup", handleMouseUp)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
document.addEventListener("mousemove", handleMouseMove)
|
|
164
|
+
document.addEventListener("mouseup", handleMouseUp)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
ref={handleRef}
|
|
170
|
+
data-panel-resize-handle
|
|
171
|
+
className={cn(
|
|
172
|
+
"relative flex items-center justify-center bg-border",
|
|
173
|
+
direction === "horizontal"
|
|
174
|
+
? "w-px cursor-col-resize hover:w-1 hover:bg-primary/50"
|
|
175
|
+
: "h-px cursor-row-resize hover:h-1 hover:bg-primary/50",
|
|
176
|
+
isDragging && (direction === "horizontal" ? "w-1 bg-primary" : "h-1 bg-primary"),
|
|
177
|
+
"transition-all",
|
|
178
|
+
className
|
|
179
|
+
)}
|
|
180
|
+
onMouseDown={handleMouseDown}
|
|
181
|
+
{...props}
|
|
182
|
+
>
|
|
183
|
+
{withHandle && (
|
|
184
|
+
<div
|
|
185
|
+
className={cn(
|
|
186
|
+
"z-10 flex items-center justify-center rounded-sm border bg-border",
|
|
187
|
+
direction === "horizontal" ? "h-4 w-3" : "h-3 w-4"
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
<svg
|
|
191
|
+
className={cn(
|
|
192
|
+
"h-2.5 w-2.5 text-muted-foreground",
|
|
193
|
+
direction === "vertical" && "rotate-90"
|
|
194
|
+
)}
|
|
195
|
+
viewBox="0 0 6 10"
|
|
196
|
+
fill="currentColor"
|
|
197
|
+
>
|
|
198
|
+
<circle cx="1" cy="2" r="1" />
|
|
199
|
+
<circle cx="1" cy="5" r="1" />
|
|
200
|
+
<circle cx="1" cy="8" r="1" />
|
|
201
|
+
<circle cx="5" cy="2" r="1" />
|
|
202
|
+
<circle cx="5" cy="5" r="1" />
|
|
203
|
+
<circle cx="5" cy="8" r="1" />
|
|
204
|
+
</svg>
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
ResizableHandle.displayName = "ResizableHandle"
|
|
212
|
+
|
|
213
|
+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
|
@@ -0,0 +1,60 @@
|
|
|
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" | "both"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ScrollArea - Custom scrollbar container
|
|
11
|
+
*
|
|
12
|
+
* Provides a styled scrollbar that is thin and consistent across browsers.
|
|
13
|
+
* Uses CSS scrollbar styling for webkit and Firefox.
|
|
14
|
+
*/
|
|
15
|
+
const ScrollArea = React.forwardRef<HTMLDivElement, ScrollAreaProps>(
|
|
16
|
+
({ className, children, orientation = "vertical", ...props }, ref) => {
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(
|
|
21
|
+
"relative",
|
|
22
|
+
// Container overflow based on orientation
|
|
23
|
+
orientation === "vertical" && "overflow-y-auto overflow-x-hidden",
|
|
24
|
+
orientation === "horizontal" && "overflow-x-auto overflow-y-hidden",
|
|
25
|
+
orientation === "both" && "overflow-auto",
|
|
26
|
+
// Custom scrollbar styling
|
|
27
|
+
"scrollbar-thin",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
ScrollArea.displayName = "ScrollArea"
|
|
38
|
+
|
|
39
|
+
// ScrollBar component for explicit scrollbar styling reference
|
|
40
|
+
interface ScrollBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
41
|
+
orientation?: "vertical" | "horizontal"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ScrollBar = React.forwardRef<HTMLDivElement, ScrollBarProps>(
|
|
45
|
+
({ className, orientation = "vertical", ...props }, ref) => (
|
|
46
|
+
<div
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(
|
|
49
|
+
"flex touch-none select-none transition-colors",
|
|
50
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
51
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
ScrollBar.displayName = "ScrollBar"
|
|
59
|
+
|
|
60
|
+
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