@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.
@@ -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 }
@@ -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
  />
@@ -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"