({
children,
variant = 'default',
size = 'md',
colorScheme = 'default',
open,
onOpenChange,
...props
})
| 221 | * Root popover component. Manages open state and folder navigation context. |
| 222 | */ |
| 223 | const Popover: React.FC<PopoverProps> = ({ |
| 224 | children, |
| 225 | variant = 'default', |
| 226 | size = 'md', |
| 227 | colorScheme = 'default', |
| 228 | open, |
| 229 | onOpenChange, |
| 230 | ...props |
| 231 | }) => { |
| 232 | const [currentFolder, setCurrentFolder] = React.useState<string | null>(null) |
| 233 | const [folderTitle, setFolderTitle] = React.useState<string | null>(null) |
| 234 | const [onFolderSelect, setOnFolderSelect] = React.useState<(() => void) | null>(null) |
| 235 | const [searchQuery, setSearchQuery] = React.useState<string>('') |
| 236 | const [lastHoveredItem, setLastHoveredItem] = React.useState<string | null>(null) |
| 237 | const [isKeyboardNav, setIsKeyboardNav] = React.useState(false) |
| 238 | const [selectedIndex, setSelectedIndex] = React.useState(-1) |
| 239 | const registeredItemsRef = React.useRef<string[]>([]) |
| 240 | |
| 241 | const registerItem = React.useCallback((id: string) => { |
| 242 | if (!registeredItemsRef.current.includes(id)) { |
| 243 | registeredItemsRef.current.push(id) |
| 244 | } |
| 245 | return registeredItemsRef.current.indexOf(id) |
| 246 | }, []) |
| 247 | |
| 248 | const unregisterItem = React.useCallback((id: string) => { |
| 249 | const index = registeredItemsRef.current.indexOf(id) |
| 250 | if (index !== -1) { |
| 251 | registeredItemsRef.current.splice(index, 1) |
| 252 | } |
| 253 | }, []) |
| 254 | |
| 255 | /** Resets all navigation state to initial values */ |
| 256 | const resetState = React.useCallback(() => { |
| 257 | setCurrentFolder(null) |
| 258 | setFolderTitle(null) |
| 259 | setOnFolderSelect(null) |
| 260 | setSearchQuery('') |
| 261 | setLastHoveredItem(null) |
| 262 | setIsKeyboardNav(false) |
| 263 | setSelectedIndex(-1) |
| 264 | registeredItemsRef.current = [] |
| 265 | }, []) |
| 266 | |
| 267 | React.useEffect(() => { |
| 268 | if (!open) { |
| 269 | resetState() |
| 270 | } |
| 271 | }, [open, resetState]) |
| 272 | |
| 273 | const handleOpenChange = React.useCallback( |
| 274 | (nextOpen: boolean) => { |
| 275 | if (nextOpen) { |
| 276 | setLastHoveredItem(null) |
| 277 | } |
| 278 | onOpenChange?.(nextOpen) |
| 279 | }, |
| 280 | [onOpenChange] |
nothing calls this directly
no test coverage detected