@orsetra/shared-ui 1.1.28 → 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
 
@@ -118,7 +120,7 @@ export function MainSidebar({
118
120
  <div
119
121
  key={item.id}
120
122
  className="relative"
121
- onMouseEnter={() => hasSubMenu && handleFlyoutMouseEnter(item.id)}
123
+ onMouseEnter={(e) => hasSubMenu && handleFlyoutMouseEnter(item.id, e.currentTarget.getBoundingClientRect())}
122
124
  onMouseLeave={() => hasSubMenu && handleFlyoutMouseLeave()}
123
125
  >
124
126
  <button
@@ -140,38 +142,43 @@ export function MainSidebar({
140
142
  )} />
141
143
  {!isMinimized && <span className="text-base">{item.label}</span>}
142
144
  </button>
143
-
144
- {hoveredMenu === item.id && hasSubMenu && (
145
- <div
146
- 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"
147
- onMouseEnter={() => handleFlyoutMouseEnter(item.id)}
148
- onMouseLeave={handleFlyoutMouseLeave}
149
- >
150
- {/* Arrow pointing left toward hovered item */}
151
- <div className="absolute -left-[5px] top-[18px] w-[10px] h-[10px] bg-white border-l border-t border-ui-border -rotate-45" />
152
- <div className="px-4 py-2 border-b border-ui-border">
153
- <h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
154
- </div>
155
- <div className="py-1">
156
- {sidebarMenus[item.id].map((subItem) => (
157
- <a
158
- key={subItem.id}
159
- href={subItem.href}
160
- onClick={(e) => handleSubMenuClick(e, subItem.href)}
161
- 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"
162
- >
163
- <subItem.icon className="h-4 w-4" />
164
- {subItem.name}
165
- </a>
166
- ))}
167
- </div>
168
- </div>
169
- )}
170
145
  </div>
171
146
  )
172
147
  })}
173
148
  </nav>
174
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
+ })()}
175
182
  </div>
176
183
  </>
177
184
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.1.28",
3
+ "version": "1.1.29",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",