@orsetra/shared-ui 1.1.27 → 1.1.29

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,14 +35,16 @@ export function MainSidebar({
35
35
  }: MainSidebarProps) {
36
36
  const isMinimized = mode === 'minimized'
37
37
  const [hoveredMenu, setHoveredMenu] = React.useState<string | null>(null)
38
+ const [flyoutPos, setFlyoutPos] = React.useState<{ top: number; left: number } | null>(null)
38
39
  const closeTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
39
40
 
40
41
  React.useEffect(() => {
41
42
  return () => { if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current) }
42
43
  }, [])
43
44
 
44
- const handleFlyoutMouseEnter = (menuId: string) => {
45
+ const handleFlyoutMouseEnter = (menuId: string, rect?: DOMRect) => {
45
46
  if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)
47
+ if (rect) setFlyoutPos({ top: rect.top, left: rect.right + 8 })
46
48
  setHoveredMenu(menuId)
47
49
  }
48
50
 
@@ -79,12 +81,6 @@ export function MainSidebar({
79
81
 
80
82
  return (
81
83
  <>
82
- {isOpen && !isMinimized && !hoveredMenu && (
83
- <div
84
- className="fixed inset-0 bg-black/50 z-40"
85
- onClick={onToggle}
86
- />
87
- )}
88
84
 
89
85
  <div className={cn(
90
86
  "fixed left-0 top-0 h-full bg-white border-r border-ui-border z-50 transform transition-all duration-300 ease-in-out",
@@ -124,7 +120,7 @@ export function MainSidebar({
124
120
  <div
125
121
  key={item.id}
126
122
  className="relative"
127
- onMouseEnter={() => hasSubMenu && handleFlyoutMouseEnter(item.id)}
123
+ onMouseEnter={(e) => hasSubMenu && handleFlyoutMouseEnter(item.id, e.currentTarget.getBoundingClientRect())}
128
124
  onMouseLeave={() => hasSubMenu && handleFlyoutMouseLeave()}
129
125
  >
130
126
  <button
@@ -146,38 +142,43 @@ export function MainSidebar({
146
142
  )} />
147
143
  {!isMinimized && <span className="text-base">{item.label}</span>}
148
144
  </button>
149
-
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" />
158
- <div className="px-4 py-2 border-b border-ui-border">
159
- <h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
160
- </div>
161
- <div className="py-1">
162
- {sidebarMenus[item.id].map((subItem) => (
163
- <a
164
- key={subItem.id}
165
- href={subItem.href}
166
- onClick={(e) => handleSubMenuClick(e, subItem.href)}
167
- className="flex items-center gap-3 px-4 py-2 text-sm text-text-secondary hover:bg-ui-background hover:text-text-primary transition-colors no-underline"
168
- >
169
- <subItem.icon className="h-4 w-4" />
170
- {subItem.name}
171
- </a>
172
- ))}
173
- </div>
174
- </div>
175
- )}
176
145
  </div>
177
146
  )
178
147
  })}
179
148
  </nav>
180
149
  </div>
150
+
151
+ {/* Flyout en position fixed : échappe overflow-y-auto et tout contexte d'empilement */}
152
+ {hoveredMenu && flyoutPos && sidebarMenus[hoveredMenu]?.length > 0 && (() => {
153
+ const activeItem = mainMenuItems.find(i => i.id === hoveredMenu)
154
+ if (!activeItem) return null
155
+ return (
156
+ <div
157
+ className="fixed bg-white border border-ui-border rounded-lg shadow-xl z-[9999] min-w-[200px] py-2"
158
+ style={{ top: flyoutPos.top, left: flyoutPos.left }}
159
+ onMouseEnter={() => handleFlyoutMouseEnter(hoveredMenu)}
160
+ onMouseLeave={handleFlyoutMouseLeave}
161
+ >
162
+ <div className="absolute -left-[5px] top-[18px] w-[10px] h-[10px] bg-white border-l border-t border-ui-border -rotate-45" />
163
+ <div className="px-4 py-2 border-b border-ui-border">
164
+ <h3 className="text-sm font-semibold text-text-primary">{activeItem.label}</h3>
165
+ </div>
166
+ <div className="py-1">
167
+ {sidebarMenus[hoveredMenu].map((subItem) => (
168
+ <a
169
+ key={subItem.id}
170
+ href={subItem.href}
171
+ onClick={(e) => handleSubMenuClick(e, subItem.href)}
172
+ className="flex items-center gap-3 px-4 py-2 text-sm text-text-secondary hover:bg-ui-background hover:text-text-primary transition-colors no-underline"
173
+ >
174
+ <subItem.icon className="h-4 w-4" />
175
+ {subItem.name}
176
+ </a>
177
+ ))}
178
+ </div>
179
+ </div>
180
+ )
181
+ })()}
181
182
  </div>
182
183
  </>
183
184
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.1.27",
3
+ "version": "1.1.29",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",
@@ -106,4 +106,4 @@
106
106
  "next": "^16.0.7",
107
107
  "typescript": "^5"
108
108
  }
109
- }
109
+ }