@srcroot/ui 0.0.54 → 0.0.56

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 (107) hide show
  1. package/README.md +151 -151
  2. package/dist/index.d.ts +0 -0
  3. package/dist/index.js +55 -1
  4. package/package.json +7 -2
  5. package/src/registry/analytics/google-analytics.tsx +36 -39
  6. package/src/registry/analytics/google-tag-manager.tsx +62 -65
  7. package/src/registry/analytics/meta-pixel.tsx +44 -47
  8. package/src/registry/analytics/microsoft-clarity.tsx +31 -34
  9. package/src/registry/analytics/tiktok-pixel.tsx +34 -37
  10. package/src/registry/lib/utils.ts +0 -0
  11. package/src/registry/themes/v3/blue.css +157 -157
  12. package/src/registry/themes/v3/glass.css +153 -153
  13. package/src/registry/themes/v3/gray.css +157 -157
  14. package/src/registry/themes/v3/green.css +157 -157
  15. package/src/registry/themes/v3/neutral.css +157 -157
  16. package/src/registry/themes/v3/orange.css +157 -157
  17. package/src/registry/themes/v3/rose.css +157 -157
  18. package/src/registry/themes/v3/slate.css +157 -157
  19. package/src/registry/themes/v3/stone.css +157 -157
  20. package/src/registry/themes/v3/violet.css +186 -186
  21. package/src/registry/themes/v3/zinc.css +157 -157
  22. package/src/registry/themes/v4/blue.css +184 -184
  23. package/src/registry/themes/v4/glass.css +180 -180
  24. package/src/registry/themes/v4/gray.css +184 -184
  25. package/src/registry/themes/v4/green.css +184 -184
  26. package/src/registry/themes/v4/neutral.css +184 -184
  27. package/src/registry/themes/v4/orange.css +184 -184
  28. package/src/registry/themes/v4/rose.css +184 -184
  29. package/src/registry/themes/v4/slate.css +184 -184
  30. package/src/registry/themes/v4/stone.css +184 -184
  31. package/src/registry/themes/v4/violet.css +184 -184
  32. package/src/registry/themes/v4/zinc.css +184 -184
  33. package/src/registry/ui/accordion.tsx +164 -165
  34. package/src/registry/ui/alert-dialog.tsx +213 -214
  35. package/src/registry/ui/alert.tsx +73 -76
  36. package/src/registry/ui/aspect-ratio.tsx +44 -47
  37. package/src/registry/ui/avatar.tsx +96 -97
  38. package/src/registry/ui/badge.tsx +52 -55
  39. package/src/registry/ui/breadcrumb.tsx +147 -150
  40. package/src/registry/ui/button-group.tsx +64 -67
  41. package/src/registry/ui/button.tsx +71 -72
  42. package/src/registry/ui/calendar.tsx +514 -515
  43. package/src/registry/ui/card.tsx +88 -91
  44. package/src/registry/ui/carousel.tsx +214 -214
  45. package/src/registry/ui/chart.tsx +373 -373
  46. package/src/registry/ui/chatbot.tsx +86 -13
  47. package/src/registry/ui/checkbox.tsx +93 -94
  48. package/src/registry/ui/collapsible.tsx +107 -108
  49. package/src/registry/ui/combobox.tsx +171 -171
  50. package/src/registry/ui/command.tsx +300 -300
  51. package/src/registry/ui/container.tsx +44 -47
  52. package/src/registry/ui/context-menu.tsx +221 -221
  53. package/src/registry/ui/date-picker.tsx +228 -228
  54. package/src/registry/ui/dialog.tsx +269 -270
  55. package/src/registry/ui/drawer.tsx +10 -4
  56. package/src/registry/ui/dropdown-menu.tsx +529 -530
  57. package/src/registry/ui/empty-state.tsx +0 -2
  58. package/src/registry/ui/file-upload.tsx +0 -0
  59. package/src/registry/ui/floating-dock.tsx +0 -0
  60. package/src/registry/ui/form-field.tsx +91 -94
  61. package/src/registry/ui/google-analytics.tsx +38 -0
  62. package/src/registry/ui/google-tag-manager.tsx +64 -0
  63. package/src/registry/ui/hover-card.tsx +223 -223
  64. package/src/registry/ui/image.tsx +144 -147
  65. package/src/registry/ui/input-group.tsx +82 -85
  66. package/src/registry/ui/input.tsx +125 -125
  67. package/src/registry/ui/kbd.tsx +60 -63
  68. package/src/registry/ui/label.tsx +36 -37
  69. package/src/registry/ui/loading-spinner.tsx +108 -111
  70. package/src/registry/ui/map.tsx +0 -0
  71. package/src/registry/ui/marquee.tsx +2 -0
  72. package/src/registry/ui/menubar.tsx +246 -246
  73. package/src/registry/ui/meta-pixel.tsx +46 -0
  74. package/src/registry/ui/microsoft-clarity.tsx +33 -0
  75. package/src/registry/ui/native-select.tsx +49 -52
  76. package/src/registry/ui/otp-input.tsx +152 -155
  77. package/src/registry/ui/pagination.tsx +149 -152
  78. package/src/registry/ui/patterns.tsx +28 -0
  79. package/src/registry/ui/popover.tsx +226 -227
  80. package/src/registry/ui/progress.tsx +51 -52
  81. package/src/registry/ui/radio.tsx +99 -102
  82. package/src/registry/ui/resizable.tsx +314 -314
  83. package/src/registry/ui/scroll-animation.tsx +45 -0
  84. package/src/registry/ui/scroll-area.tsx +121 -122
  85. package/src/registry/ui/scroll-to-top.tsx +0 -0
  86. package/src/registry/ui/search.tsx +147 -150
  87. package/src/registry/ui/select.tsx +292 -293
  88. package/src/registry/ui/separator.tsx +46 -47
  89. package/src/registry/ui/sheet.tsx +6 -3
  90. package/src/registry/ui/sidebar.tsx +628 -628
  91. package/src/registry/ui/skeleton.tsx +26 -29
  92. package/src/registry/ui/slider.tsx +196 -197
  93. package/src/registry/ui/slot.tsx +69 -72
  94. package/src/registry/ui/star-rating.tsx +131 -134
  95. package/src/registry/ui/switch.tsx +72 -73
  96. package/src/registry/ui/table-of-contents.tsx +96 -96
  97. package/src/registry/ui/table.tsx +138 -139
  98. package/src/registry/ui/tabs.tsx +124 -125
  99. package/src/registry/ui/text.tsx +61 -64
  100. package/src/registry/ui/textarea.tsx +41 -42
  101. package/src/registry/ui/theme-switcher.tsx +66 -66
  102. package/src/registry/ui/tiktok-pixel.tsx +36 -0
  103. package/src/registry/ui/toast.tsx +97 -98
  104. package/src/registry/ui/toggle-group.tsx +129 -129
  105. package/src/registry/ui/toggle.tsx +72 -72
  106. package/src/registry/ui/tooltip.tsx +143 -144
  107. package/src/registry/ui/whatsapp.tsx +0 -0
@@ -1,314 +1,314 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { cn } from "@/lib/utils"
5
-
6
- // Resizable Context
7
- interface ResizablePanelGroupContextValue {
8
- groupId: string
9
- direction: "horizontal" | "vertical"
10
- registerPanel: (defaultSize: number, minSize: number, maxSize: number) => number
11
- getSize: (index: number) => number
12
- getTotalSize: () => number
13
- onResize: (handleIndex: number, delta: number) => void
14
- }
15
-
16
- const ResizablePanelGroupContext = React.createContext<ResizablePanelGroupContextValue | null>(null)
17
-
18
- function useResizablePanelGroup() {
19
- const context = React.useContext(ResizablePanelGroupContext)
20
- if (!context) {
21
- throw new Error("useResizablePanelGroup must be used within a ResizablePanelGroup")
22
- }
23
- return context
24
- }
25
-
26
- // ResizablePanelGroup
27
- interface ResizablePanelGroupProps extends React.HTMLAttributes<HTMLDivElement> {
28
- direction?: "horizontal" | "vertical"
29
- onLayout?: (sizes: number[]) => void
30
- }
31
-
32
- interface PanelConfig {
33
- defaultSize: number
34
- minSize: number
35
- maxSize: number
36
- }
37
-
38
- const ResizablePanelGroup = React.forwardRef<HTMLDivElement, ResizablePanelGroupProps>(
39
- ({ className, direction = "horizontal", children, onLayout, ...props }, ref) => {
40
- const groupId = React.useId()
41
- const containerRef = React.useRef<HTMLDivElement>(null)
42
- const [, forceUpdate] = React.useReducer(x => x + 1, 0)
43
-
44
- // Store panel configs and sizes in refs for stable access
45
- const panelConfigsRef = React.useRef<PanelConfig[]>([])
46
- const sizesRef = React.useRef<number[]>([])
47
- const panelCountRef = React.useRef(0)
48
-
49
- // Reset panel count at start of each render
50
- panelCountRef.current = 0
51
-
52
- const registerPanel = React.useCallback((defaultSize: number, minSize: number, maxSize: number) => {
53
- const index = panelCountRef.current++
54
-
55
- // Only initialize if not already set
56
- if (sizesRef.current[index] === undefined) {
57
- sizesRef.current[index] = defaultSize
58
- panelConfigsRef.current[index] = { defaultSize, minSize, maxSize }
59
- }
60
-
61
- return index
62
- }, [])
63
-
64
- const getSize = React.useCallback((index: number) => {
65
- return sizesRef.current[index] ?? 50
66
- }, [])
67
-
68
- const getTotalSize = React.useCallback(() => {
69
- return sizesRef.current.reduce((sum, s) => sum + s, 0)
70
- }, [])
71
-
72
- const onResize = React.useCallback((handleIndex: number, delta: number) => {
73
- const container = containerRef.current
74
- if (!container) return
75
-
76
- const containerSize = direction === "horizontal"
77
- ? container.offsetWidth
78
- : container.offsetHeight
79
-
80
- if (containerSize === 0) return
81
-
82
- const deltaPercent = (delta / containerSize) * 100
83
-
84
- const leftIndex = handleIndex
85
- const rightIndex = handleIndex + 1
86
- const sizes = sizesRef.current
87
- const configs = panelConfigsRef.current
88
-
89
- if (leftIndex >= sizes.length || rightIndex >= sizes.length) return
90
-
91
- const leftSize = sizes[leftIndex]
92
- const rightSize = sizes[rightIndex]
93
- const totalSize = leftSize + rightSize
94
-
95
- let newLeftSize = leftSize + deltaPercent
96
- let newRightSize = rightSize - deltaPercent
97
-
98
- const leftMin = configs[leftIndex]?.minSize ?? 10
99
- const leftMax = configs[leftIndex]?.maxSize ?? 90
100
- const rightMin = configs[rightIndex]?.minSize ?? 10
101
- const rightMax = configs[rightIndex]?.maxSize ?? 90
102
-
103
- // Apply constraints
104
- if (newLeftSize < leftMin) {
105
- newLeftSize = leftMin
106
- newRightSize = totalSize - leftMin
107
- }
108
- if (newRightSize < rightMin) {
109
- newRightSize = rightMin
110
- newLeftSize = totalSize - rightMin
111
- }
112
- if (newLeftSize > leftMax) {
113
- newLeftSize = leftMax
114
- newRightSize = totalSize - leftMax
115
- }
116
- if (newRightSize > rightMax) {
117
- newRightSize = rightMax
118
- newLeftSize = totalSize - rightMax
119
- }
120
-
121
- sizesRef.current[leftIndex] = newLeftSize
122
- sizesRef.current[rightIndex] = newRightSize
123
-
124
- // Force re-render to update panel sizes
125
- forceUpdate()
126
-
127
- // Notify layout changes
128
- onLayout?.([...sizesRef.current])
129
- }, [direction, onLayout])
130
-
131
- return (
132
- <ResizablePanelGroupContext.Provider value={{ groupId, direction, registerPanel, getSize, getTotalSize, onResize }}>
133
- <div
134
- ref={(node) => {
135
- containerRef.current = node
136
- if (typeof ref === "function") ref(node)
137
- else if (ref) ref.current = node
138
- }}
139
- data-panel-group
140
- data-panel-group-id={groupId}
141
- data-direction={direction}
142
- className={cn(
143
- "flex h-full w-full",
144
- direction === "horizontal" ? "flex-row" : "flex-col",
145
- className
146
- )}
147
- {...props}
148
- >
149
- {children}
150
- </div>
151
- </ResizablePanelGroupContext.Provider>
152
- )
153
- }
154
- )
155
- ResizablePanelGroup.displayName = "ResizablePanelGroup"
156
-
157
- // ResizablePanel
158
- interface ResizablePanelProps extends React.HTMLAttributes<HTMLDivElement> {
159
- defaultSize?: number
160
- minSize?: number
161
- maxSize?: number
162
- }
163
-
164
- const ResizablePanel = React.forwardRef<HTMLDivElement, ResizablePanelProps>(
165
- ({ className, defaultSize = 50, minSize = 10, maxSize = 90, children, style, ...props }, ref) => {
166
- const { groupId, direction, registerPanel, getSize, getTotalSize } = useResizablePanelGroup()
167
- const indexRef = React.useRef<number>(-1)
168
-
169
- // Register panel on first render only
170
- if (indexRef.current === -1) {
171
- indexRef.current = registerPanel(defaultSize, minSize, maxSize)
172
- }
173
-
174
- const size = getSize(indexRef.current)
175
- const totalSize = getTotalSize()
176
- // Convert to actual percentage of total
177
- // const actualPercent = totalSize > 0 ? (size / totalSize) * 100 : size
178
-
179
- return (
180
- <div
181
- ref={ref}
182
- data-panel
183
- data-panel-group-id={groupId}
184
- data-panel-index={indexRef.current}
185
- className={cn("overflow-hidden", className)}
186
- style={{
187
- ...style,
188
- // Use flex-grow with the size as the ratio for smoother resizing
189
- flex: `${size} 1 0`,
190
- }}
191
- {...props}
192
- >
193
- {children}
194
- </div>
195
- )
196
- }
197
- )
198
- ResizablePanel.displayName = "ResizablePanel"
199
-
200
- // ResizableHandle
201
- interface ResizableHandleProps extends React.HTMLAttributes<HTMLDivElement> {
202
- withHandle?: boolean
203
- }
204
-
205
- const ResizableHandle = React.forwardRef<HTMLDivElement, ResizableHandleProps>(
206
- ({ className, withHandle = false, ...props }, ref) => {
207
- const { direction, onResize } = useResizablePanelGroup()
208
- const [isDragging, setIsDragging] = React.useState(false)
209
- const handleIndexRef = React.useRef<number>(-1)
210
- const lastPosRef = React.useRef<number>(0)
211
- const handleRef = React.useRef<HTMLDivElement>(null)
212
-
213
- // Determine handle index from DOM position
214
- const getHandleIndex = React.useCallback(() => {
215
- const handle = handleRef.current
216
- if (!handle) return 0
217
-
218
- const parent = handle.parentElement
219
- if (!parent) return 0
220
-
221
- let handleCount = 0
222
- for (const child of Array.from(parent.children)) {
223
- if (child === handle) break
224
- if (child.hasAttribute('data-panel-resize-handle')) {
225
- handleCount++
226
- }
227
- }
228
- return handleCount
229
- }, [])
230
-
231
- const handleMouseDown = React.useCallback((e: React.MouseEvent) => {
232
- e.preventDefault()
233
- e.stopPropagation()
234
- setIsDragging(true)
235
-
236
- const handleIndex = getHandleIndex()
237
- const startPos = direction === "horizontal" ? e.clientX : e.clientY
238
- lastPosRef.current = startPos
239
-
240
- const handleMouseMove = (moveEvent: MouseEvent) => {
241
- const currentPos = direction === "horizontal" ? moveEvent.clientX : moveEvent.clientY
242
- const delta = currentPos - lastPosRef.current
243
- lastPosRef.current = currentPos
244
-
245
- if (delta !== 0) {
246
- onResize(handleIndex, delta)
247
- }
248
- }
249
-
250
- const handleMouseUp = () => {
251
- setIsDragging(false)
252
- document.removeEventListener("mousemove", handleMouseMove)
253
- document.removeEventListener("mouseup", handleMouseUp)
254
- document.body.style.cursor = ""
255
- document.body.style.userSelect = ""
256
- }
257
-
258
- document.body.style.cursor = direction === "horizontal" ? "col-resize" : "row-resize"
259
- document.body.style.userSelect = "none"
260
- document.addEventListener("mousemove", handleMouseMove)
261
- document.addEventListener("mouseup", handleMouseUp)
262
- }, [direction, onResize, getHandleIndex])
263
-
264
- return (
265
- <div
266
- ref={(node) => {
267
- handleRef.current = node
268
- if (typeof ref === "function") ref(node)
269
- else if (ref) ref.current = node
270
- }}
271
- data-panel-resize-handle
272
- data-dragging={isDragging}
273
- className={cn(
274
- "relative flex shrink-0 items-center justify-center bg-border",
275
- direction === "horizontal"
276
- ? "w-1 cursor-col-resize hover:bg-primary/50 active:bg-primary"
277
- : "h-1 cursor-row-resize hover:bg-primary/50 active:bg-primary",
278
- isDragging && "bg-primary",
279
- className
280
- )}
281
- onMouseDown={handleMouseDown}
282
- {...props}
283
- >
284
- {withHandle && (
285
- <div
286
- className={cn(
287
- "z-10 flex items-center justify-center rounded-sm border bg-border",
288
- direction === "horizontal" ? "h-4 w-3" : "h-3 w-4"
289
- )}
290
- >
291
- <svg
292
- className={cn(
293
- "h-2.5 w-2.5 text-muted-foreground",
294
- direction === "vertical" && "rotate-90"
295
- )}
296
- viewBox="0 0 6 10"
297
- fill="currentColor"
298
- >
299
- <circle cx="1" cy="2" r="1" />
300
- <circle cx="1" cy="5" r="1" />
301
- <circle cx="1" cy="8" r="1" />
302
- <circle cx="5" cy="2" r="1" />
303
- <circle cx="5" cy="5" r="1" />
304
- <circle cx="5" cy="8" r="1" />
305
- </svg>
306
- </div>
307
- )}
308
- </div>
309
- )
310
- }
311
- )
312
- ResizableHandle.displayName = "ResizableHandle"
313
-
314
- export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ // Resizable Context
7
+ interface ResizablePanelGroupContextValue {
8
+ groupId: string
9
+ direction: "horizontal" | "vertical"
10
+ registerPanel: (defaultSize: number, minSize: number, maxSize: number) => number
11
+ getSize: (index: number) => number
12
+ getTotalSize: () => number
13
+ onResize: (handleIndex: number, delta: number) => void
14
+ }
15
+
16
+ const ResizablePanelGroupContext = React.createContext<ResizablePanelGroupContextValue | null>(null)
17
+
18
+ function useResizablePanelGroup() {
19
+ const context = React.useContext(ResizablePanelGroupContext)
20
+ if (!context) {
21
+ throw new Error("useResizablePanelGroup must be used within a ResizablePanelGroup")
22
+ }
23
+ return context
24
+ }
25
+
26
+ // ResizablePanelGroup
27
+ interface ResizablePanelGroupProps extends React.HTMLAttributes<HTMLDivElement> {
28
+ direction?: "horizontal" | "vertical"
29
+ onLayout?: (sizes: number[]) => void
30
+ }
31
+
32
+ interface PanelConfig {
33
+ defaultSize: number
34
+ minSize: number
35
+ maxSize: number
36
+ }
37
+
38
+ const ResizablePanelGroup = React.forwardRef<HTMLDivElement, ResizablePanelGroupProps>(
39
+ ({ className, direction = "horizontal", children, onLayout, ...props }, ref) => {
40
+ const groupId = React.useId()
41
+ const containerRef = React.useRef<HTMLDivElement>(null)
42
+ const [, forceUpdate] = React.useReducer(x => x + 1, 0)
43
+
44
+ // Store panel configs and sizes in refs for stable access
45
+ const panelConfigsRef = React.useRef<PanelConfig[]>([])
46
+ const sizesRef = React.useRef<number[]>([])
47
+ const panelCountRef = React.useRef(0)
48
+
49
+ // Reset panel count at start of each render
50
+ panelCountRef.current = 0
51
+
52
+ const registerPanel = React.useCallback((defaultSize: number, minSize: number, maxSize: number) => {
53
+ const index = panelCountRef.current++
54
+
55
+ // Only initialize if not already set
56
+ if (sizesRef.current[index] === undefined) {
57
+ sizesRef.current[index] = defaultSize
58
+ panelConfigsRef.current[index] = { defaultSize, minSize, maxSize }
59
+ }
60
+
61
+ return index
62
+ }, [])
63
+
64
+ const getSize = React.useCallback((index: number) => {
65
+ return sizesRef.current[index] ?? 50
66
+ }, [])
67
+
68
+ const getTotalSize = React.useCallback(() => {
69
+ return sizesRef.current.reduce((sum, s) => sum + s, 0)
70
+ }, [])
71
+
72
+ const onResize = React.useCallback((handleIndex: number, delta: number) => {
73
+ const container = containerRef.current
74
+ if (!container) return
75
+
76
+ const containerSize = direction === "horizontal"
77
+ ? container.offsetWidth
78
+ : container.offsetHeight
79
+
80
+ if (containerSize === 0) return
81
+
82
+ const deltaPercent = (delta / containerSize) * 100
83
+
84
+ const leftIndex = handleIndex
85
+ const rightIndex = handleIndex + 1
86
+ const sizes = sizesRef.current
87
+ const configs = panelConfigsRef.current
88
+
89
+ if (leftIndex >= sizes.length || rightIndex >= sizes.length) return
90
+
91
+ const leftSize = sizes[leftIndex]
92
+ const rightSize = sizes[rightIndex]
93
+ const totalSize = leftSize + rightSize
94
+
95
+ let newLeftSize = leftSize + deltaPercent
96
+ let newRightSize = rightSize - deltaPercent
97
+
98
+ const leftMin = configs[leftIndex]?.minSize ?? 10
99
+ const leftMax = configs[leftIndex]?.maxSize ?? 90
100
+ const rightMin = configs[rightIndex]?.minSize ?? 10
101
+ const rightMax = configs[rightIndex]?.maxSize ?? 90
102
+
103
+ // Apply constraints
104
+ if (newLeftSize < leftMin) {
105
+ newLeftSize = leftMin
106
+ newRightSize = totalSize - leftMin
107
+ }
108
+ if (newRightSize < rightMin) {
109
+ newRightSize = rightMin
110
+ newLeftSize = totalSize - rightMin
111
+ }
112
+ if (newLeftSize > leftMax) {
113
+ newLeftSize = leftMax
114
+ newRightSize = totalSize - leftMax
115
+ }
116
+ if (newRightSize > rightMax) {
117
+ newRightSize = rightMax
118
+ newLeftSize = totalSize - rightMax
119
+ }
120
+
121
+ sizesRef.current[leftIndex] = newLeftSize
122
+ sizesRef.current[rightIndex] = newRightSize
123
+
124
+ // Force re-render to update panel sizes
125
+ forceUpdate()
126
+
127
+ // Notify layout changes
128
+ onLayout?.([...sizesRef.current])
129
+ }, [direction, onLayout])
130
+
131
+ return (
132
+ <ResizablePanelGroupContext.Provider value={{ groupId, direction, registerPanel, getSize, getTotalSize, onResize }}>
133
+ <div
134
+ ref={(node) => {
135
+ containerRef.current = node
136
+ if (typeof ref === "function") ref(node)
137
+ else if (ref) ref.current = node
138
+ }}
139
+ data-panel-group
140
+ data-panel-group-id={groupId}
141
+ data-direction={direction}
142
+ className={cn(
143
+ "flex h-full w-full",
144
+ direction === "horizontal" ? "flex-row" : "flex-col",
145
+ className
146
+ )}
147
+ {...props}
148
+ >
149
+ {children}
150
+ </div>
151
+ </ResizablePanelGroupContext.Provider>
152
+ )
153
+ }
154
+ )
155
+ ResizablePanelGroup.displayName = "ResizablePanelGroup"
156
+
157
+ // ResizablePanel
158
+ interface ResizablePanelProps extends React.HTMLAttributes<HTMLDivElement> {
159
+ defaultSize?: number
160
+ minSize?: number
161
+ maxSize?: number
162
+ }
163
+
164
+ const ResizablePanel = React.forwardRef<HTMLDivElement, ResizablePanelProps>(
165
+ ({ className, defaultSize = 50, minSize = 10, maxSize = 90, children, style, ...props }, ref) => {
166
+ const { groupId, direction, registerPanel, getSize, getTotalSize } = useResizablePanelGroup()
167
+ const indexRef = React.useRef<number>(-1)
168
+
169
+ // Register panel on first render only
170
+ if (indexRef.current === -1) {
171
+ indexRef.current = registerPanel(defaultSize, minSize, maxSize)
172
+ }
173
+
174
+ const size = getSize(indexRef.current)
175
+ const totalSize = getTotalSize()
176
+ // Convert to actual percentage of total
177
+ // const actualPercent = totalSize > 0 ? (size / totalSize) * 100 : size
178
+
179
+ return (
180
+ <div
181
+ ref={ref}
182
+ data-panel
183
+ data-panel-group-id={groupId}
184
+ data-panel-index={indexRef.current}
185
+ className={cn("overflow-hidden", className)}
186
+ style={{
187
+ ...style,
188
+ // Use flex-grow with the size as the ratio for smoother resizing
189
+ flex: `${size} 1 0`,
190
+ }}
191
+ {...props}
192
+ >
193
+ {children}
194
+ </div>
195
+ )
196
+ }
197
+ )
198
+ ResizablePanel.displayName = "ResizablePanel"
199
+
200
+ // ResizableHandle
201
+ interface ResizableHandleProps extends React.HTMLAttributes<HTMLDivElement> {
202
+ withHandle?: boolean
203
+ }
204
+
205
+ const ResizableHandle = React.forwardRef<HTMLDivElement, ResizableHandleProps>(
206
+ ({ className, withHandle = false, ...props }, ref) => {
207
+ const { direction, onResize } = useResizablePanelGroup()
208
+ const [isDragging, setIsDragging] = React.useState(false)
209
+ const handleIndexRef = React.useRef<number>(-1)
210
+ const lastPosRef = React.useRef<number>(0)
211
+ const handleRef = React.useRef<HTMLDivElement>(null)
212
+
213
+ // Determine handle index from DOM position
214
+ const getHandleIndex = React.useCallback(() => {
215
+ const handle = handleRef.current
216
+ if (!handle) return 0
217
+
218
+ const parent = handle.parentElement
219
+ if (!parent) return 0
220
+
221
+ let handleCount = 0
222
+ for (const child of Array.from(parent.children)) {
223
+ if (child === handle) break
224
+ if (child.hasAttribute('data-panel-resize-handle')) {
225
+ handleCount++
226
+ }
227
+ }
228
+ return handleCount
229
+ }, [])
230
+
231
+ const handleMouseDown = React.useCallback((e: React.MouseEvent) => {
232
+ e.preventDefault()
233
+ e.stopPropagation()
234
+ setIsDragging(true)
235
+
236
+ const handleIndex = getHandleIndex()
237
+ const startPos = direction === "horizontal" ? e.clientX : e.clientY
238
+ lastPosRef.current = startPos
239
+
240
+ const handleMouseMove = (moveEvent: MouseEvent) => {
241
+ const currentPos = direction === "horizontal" ? moveEvent.clientX : moveEvent.clientY
242
+ const delta = currentPos - lastPosRef.current
243
+ lastPosRef.current = currentPos
244
+
245
+ if (delta !== 0) {
246
+ onResize(handleIndex, delta)
247
+ }
248
+ }
249
+
250
+ const handleMouseUp = () => {
251
+ setIsDragging(false)
252
+ document.removeEventListener("mousemove", handleMouseMove)
253
+ document.removeEventListener("mouseup", handleMouseUp)
254
+ document.body.style.cursor = ""
255
+ document.body.style.userSelect = ""
256
+ }
257
+
258
+ document.body.style.cursor = direction === "horizontal" ? "col-resize" : "row-resize"
259
+ document.body.style.userSelect = "none"
260
+ document.addEventListener("mousemove", handleMouseMove)
261
+ document.addEventListener("mouseup", handleMouseUp)
262
+ }, [direction, onResize, getHandleIndex])
263
+
264
+ return (
265
+ <div
266
+ ref={(node) => {
267
+ handleRef.current = node
268
+ if (typeof ref === "function") ref(node)
269
+ else if (ref) ref.current = node
270
+ }}
271
+ data-panel-resize-handle
272
+ data-dragging={isDragging}
273
+ className={cn(
274
+ "relative flex shrink-0 items-center justify-center bg-border",
275
+ direction === "horizontal"
276
+ ? "w-1 cursor-col-resize hover:bg-primary/50 active:bg-primary"
277
+ : "h-1 cursor-row-resize hover:bg-primary/50 active:bg-primary",
278
+ isDragging && "bg-primary",
279
+ className
280
+ )}
281
+ onMouseDown={handleMouseDown}
282
+ {...props}
283
+ >
284
+ {withHandle && (
285
+ <div
286
+ className={cn(
287
+ "z-10 flex items-center justify-center rounded-sm border bg-border",
288
+ direction === "horizontal" ? "h-4 w-3" : "h-3 w-4"
289
+ )}
290
+ >
291
+ <svg
292
+ className={cn(
293
+ "h-2.5 w-2.5 text-muted-foreground",
294
+ direction === "vertical" && "rotate-90"
295
+ )}
296
+ viewBox="0 0 6 10"
297
+ fill="currentColor"
298
+ >
299
+ <circle cx="1" cy="2" r="1" />
300
+ <circle cx="1" cy="5" r="1" />
301
+ <circle cx="1" cy="8" r="1" />
302
+ <circle cx="5" cy="2" r="1" />
303
+ <circle cx="5" cy="5" r="1" />
304
+ <circle cx="5" cy="8" r="1" />
305
+ </svg>
306
+ </div>
307
+ )}
308
+ </div>
309
+ )
310
+ }
311
+ )
312
+ ResizableHandle.displayName = "ResizableHandle"
313
+
314
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle }