import { useCallback, useEffect, useRef } from 'react'; import { useWixPatternsContainer } from '@wix/bex-core/react'; export type OpenCollectionOverlay = (onReturnToForeground?: () => void) => void; /** * Builds an opener that navigates to a collection (by id) as an overlay on top * of the current page, and invokes `onReturnToForeground` once the overlay * closes and the page returns to the foreground. * * Must be called from a component that outlives the inline cell editor (e.g. a * cell type's `EditWrapper`): opening the overlay typically commits/unmounts * the editor, so the layer-state subscription is owned here and disposed only * on this owner's unmount (and before re-opening). The editor passes the * refresh it wants to run on return as `onReturnToForeground` when it calls the * opener, since the editor — not this owner — holds the data to refresh. * * Returns `undefined` when the host can't navigate (no `collectionId`, * `navigateTo`, or page location). */ export function useOpenCollectionOverlay({ collectionId, }: { collectionId: string | undefined; }): OpenCollectionOverlay | undefined { const container = useWixPatternsContainer(); const pageLocation = container.usePageLocation?.(); const navigateTo = container.navigateTo; const onLayerStateChange = container.onLayerStateChange; // The subscription is owned by this (stable) component, not the editor that // triggers it, so it survives the editor unmounting when the overlay opens // and is disposed on this owner's unmount (or before re-opening). const subscriptionRef = useRef<{ remove: () => void } | null>(null); useEffect( () => () => { subscriptionRef.current?.remove(); subscriptionRef.current = null; }, [], ); const open = useCallback( (onReturnToForeground) => { if (!collectionId || !navigateTo || !pageLocation) { return; } // Preserve the current route (pathname, existing query params, hash) and // only swap in the referenced collectionId, so nested routes and required // query state survive the navigation. const params = new URLSearchParams(pageLocation.search); params.set('collectionId', collectionId); navigateTo({ pageId: pageLocation.pageId, relativeUrl: `${pageLocation.pathname}?${params.toString()}${ pageLocation.hash ?? '' }`, displayMode: 'overlay', history: 'replace', }); subscriptionRef.current?.remove(); subscriptionRef.current = onLayerStateChange?.((newState) => { if (newState === 'foreground') { subscriptionRef.current?.remove(); subscriptionRef.current = null; onReturnToForeground?.(); } }) ?? null; }, [collectionId, navigateTo, pageLocation, onLayerStateChange], ); const canOpen = Boolean(collectionId && navigateTo && pageLocation); return canOpen ? open : undefined; }