@orsetra/shared-ui 1.1.24 → 1.1.26

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.
@@ -35,6 +35,20 @@ export function MainSidebar({
35
35
  }: MainSidebarProps) {
36
36
  const isMinimized = mode === 'minimized'
37
37
  const [hoveredMenu, setHoveredMenu] = React.useState<string | null>(null)
38
+ const closeTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
39
+
40
+ React.useEffect(() => {
41
+ return () => { if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current) }
42
+ }, [])
43
+
44
+ const handleFlyoutMouseEnter = (menuId: string) => {
45
+ if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)
46
+ setHoveredMenu(menuId)
47
+ }
48
+
49
+ const handleFlyoutMouseLeave = () => {
50
+ closeTimeoutRef.current = setTimeout(() => setHoveredMenu(null), 150)
51
+ }
38
52
 
39
53
  const handleMenuClick = (menuId: string) => {
40
54
  onMenuSelect(menuId)
@@ -43,21 +57,12 @@ export function MainSidebar({
43
57
  onSecondarySidebarOpen()
44
58
  }
45
59
 
46
- const subMenus = sidebarMenus[menuId]
47
- if (subMenus && subMenus.length > 0) {
48
- const targetUrl = subMenus[0].href
49
- const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
50
- ? new URL(targetUrl).pathname
51
- : targetUrl
52
- window.location.href = path
53
- } else {
54
60
  const targetUrl = `${main_base_url}/${menuId}`
55
61
  const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
56
62
  ? new URL(targetUrl).pathname
57
63
  : targetUrl
58
64
  window.location.href = path
59
- }
60
-
65
+
61
66
  if (!isMinimized) {
62
67
  onToggle()
63
68
  }
@@ -114,12 +119,13 @@ export function MainSidebar({
114
119
  <nav className="space-y-2">
115
120
  {mainMenuItems.map((item) => {
116
121
  const Icon = item.icon
122
+ const hasSubMenu = sidebarMenus[item.id]?.length > 0
117
123
  return (
118
124
  <div
119
125
  key={item.id}
120
126
  className="relative"
121
- onMouseEnter={() => isMinimized && setHoveredMenu(item.id)}
122
- onMouseLeave={() => isMinimized && setHoveredMenu(null)}
127
+ onMouseEnter={() => hasSubMenu && handleFlyoutMouseEnter(item.id)}
128
+ onMouseLeave={() => hasSubMenu && handleFlyoutMouseLeave()}
123
129
  >
124
130
  <button
125
131
  onClick={() => handleMenuClick(item.id)}
@@ -141,8 +147,14 @@ export function MainSidebar({
141
147
  {!isMinimized && <span className="text-base">{item.label}</span>}
142
148
  </button>
143
149
 
144
- {isMinimized && hoveredMenu === item.id && sidebarMenus[item.id] && sidebarMenus[item.id].length > 0 && (
145
- <div className="absolute left-full top-0 ml-2 bg-white border border-ui-border rounded-lg shadow-xl z-50 min-w-[200px] py-2">
150
+ {hoveredMenu === item.id && hasSubMenu && (
151
+ <div
152
+ className="absolute left-full top-0 ml-2 bg-white border border-ui-border rounded-lg shadow-xl z-50 min-w-[200px] py-2"
153
+ onMouseEnter={() => handleFlyoutMouseEnter(item.id)}
154
+ onMouseLeave={handleFlyoutMouseLeave}
155
+ >
156
+ {/* Arrow pointing left toward hovered item */}
157
+ <div className="absolute -left-[5px] top-[18px] w-[10px] h-[10px] bg-white border-l border-t border-ui-border -rotate-45" />
146
158
  <div className="px-4 py-2 border-b border-ui-border">
147
159
  <h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
148
160
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",