diff --git a/packages/react/navigation-menu/src/NavigationMenu.stories.tsx b/packages/react/navigation-menu/src/NavigationMenu.stories.tsx index e8badc91e2..cb79f9a969 100644 --- a/packages/react/navigation-menu/src/NavigationMenu.stories.tsx +++ b/packages/react/navigation-menu/src/NavigationMenu.stories.tsx @@ -369,6 +369,55 @@ export const Submenus = () => { ); }; +export const WithPortalContainer = () => { + const [portalContainer, setPortalContainer] = React.useState<HTMLDivElement | null>(null); + + return ( + <StoryFrame> + <NavigationMenu.Root> + <div data-portal-container="" ref={setPortalContainer} /> + <NavigationMenu.List className={mainListClass()}> + <NavigationMenu.Item className={expandableItemClass()}> + <TriggerWithIndicator>Portaled Products</TriggerWithIndicator> + <NavigationMenu.Portal container={portalContainer}> + <NavigationMenu.Content className={basicContentClass()}> + <LinkGroup + bordered={false} + items={[ + 'Fusce pellentesque', + 'Aliquam porttitor', + 'Pellentesque', + 'Fusce pellentesque', + 'Aliquam porttitor', + 'Pellentesque', + ]} + /> + </NavigationMenu.Content> + </NavigationMenu.Portal> + </NavigationMenu.Item> + + <NavigationMenu.Item className={expandableItemClass()}> + <TriggerWithIndicator>Not Portaled Products</TriggerWithIndicator> + <NavigationMenu.Content className={basicContentClass()}> + <LinkGroup + bordered={false} + items={[ + 'Fusce pellentesque', + 'Aliquam porttitor', + 'Pellentesque', + 'Fusce pellentesque', + 'Aliquam porttitor', + 'Pellentesque', + ]} + /> + </NavigationMenu.Content> + </NavigationMenu.Item> + </NavigationMenu.List> + </NavigationMenu.Root> + </StoryFrame> + ); +}; + /* -----------------------------------------------------------------------------------------------*/ const StoryFrame: React.FC<{ children: React.ReactNode }> = ({ children }) => { diff --git a/packages/react/navigation-menu/src/NavigationMenu.tsx b/packages/react/navigation-menu/src/NavigationMenu.tsx index e450ce8dba..3725e76176 100644 --- a/packages/react/navigation-menu/src/NavigationMenu.tsx +++ b/packages/react/navigation-menu/src/NavigationMenu.tsx @@ -16,6 +16,7 @@ import { usePrevious } from '@radix-ui/react-use-previous'; import { useLayoutEffect } from '@radix-ui/react-use-layout-effect'; import { useCallbackRef } from '@radix-ui/react-use-callback-ref'; import * as VisuallyHiddenPrimitive from '@radix-ui/react-visually-hidden'; +import { Portal as PortalPrimitive } from '@radix-ui/react-portal'; import type { Scope } from '@radix-ui/react-context'; @@ -1100,6 +1101,49 @@ const FocusGroup = React.forwardRef<FocusGroupElement, FocusGroupProps>( } ); +/* ------------------------------------------------------------------------------------------------- + * NaviationMenuPortal + * -----------------------------------------------------------------------------------------------*/ + +const PORTAL_NAME = 'NavigationMenuPortal'; + +type PortalContextValue = { forceMount?: true }; +const [PortalProvider] = createNavigationMenuContext<PortalContextValue>(PORTAL_NAME, { + forceMount: undefined, +}); + +type PortalProps = React.ComponentPropsWithoutRef<typeof PortalPrimitive>; +interface NavigationMenuPortalProps { + children?: React.ReactNode; + /** + * Specify a container element to portal the content into. + */ + container?: PortalProps['container']; + /** + * Used to force mounting when more control is needed. Useful when + * controlling animation with React animation libraries. + */ + forceMount?: true; +} + +const NavigationMenuPortal: React.FC<NavigationMenuPortalProps> = ( + props: ScopedProps<NavigationMenuContentProps> +) => { + const { __scopeNavigationMenu, forceMount, children } = props; + const context = useNavigationMenuContext(PORTAL_NAME, __scopeNavigationMenu); + return ( + <PortalProvider scope={__scopeNavigationMenu} forceMount={forceMount}> + {React.Children.map(children, (child) => ( + <Presence present={forceMount || context.isRootMenu}> + <PortalPrimitive asChild>{child}</PortalPrimitive> + </Presence> + ))} + </PortalProvider> + ); +}; + +NavigationMenuPortal.displayName = PORTAL_NAME; + /* -----------------------------------------------------------------------------------------------*/ const ARROW_KEYS = ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown']; @@ -1249,6 +1293,7 @@ const Link = NavigationMenuLink; const Indicator = NavigationMenuIndicator; const Content = NavigationMenuContent; const Viewport = NavigationMenuViewport; +const Portal = NavigationMenuPortal; export { createNavigationMenuScope, @@ -1262,6 +1307,7 @@ export { NavigationMenuIndicator, NavigationMenuContent, NavigationMenuViewport, + NavigationMenuPortal, // Root, Sub, @@ -1272,6 +1318,7 @@ export { Indicator, Content, Viewport, + Portal, }; export type { NavigationMenuProps, @@ -1283,4 +1330,5 @@ export type { NavigationMenuIndicatorProps, NavigationMenuContentProps, NavigationMenuViewportProps, + NavigationMenuPortalProps, }; diff --git a/packages/react/navigation-menu/src/index.ts b/packages/react/navigation-menu/src/index.ts index 3183133ff1..8e1f9b68b8 100644 --- a/packages/react/navigation-menu/src/index.ts +++ b/packages/react/navigation-menu/src/index.ts @@ -11,6 +11,7 @@ export { NavigationMenuIndicator, NavigationMenuContent, NavigationMenuViewport, + NavigationMenuPortal, // Root, Sub, @@ -21,6 +22,7 @@ export { Indicator, Content, Viewport, + Portal, } from './NavigationMenu'; export type { NavigationMenuProps, @@ -32,4 +34,5 @@ export type { NavigationMenuIndicatorProps, NavigationMenuContentProps, NavigationMenuViewportProps, + NavigationMenuPortalProps, } from './NavigationMenu'; diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000000..927fc25700 --- /dev/null +++ b/readme.txt @@ -0,0 +1,24 @@ +Commencer par git clone le repo : https://github.com/zephyr-dassouli/radixui-primitives +On a créé une branche pour chaque composant. +Choisir la branche du composant à tester avec git checkout. + +1) Se rendre dans le dossier ssr-testing du repo + +2) Installation de Node.js +sudo apt install npm + +3) Installation de yarn +sudo npm install -g yarn + +4) Setup de yarn dans le repo +sudo yarn install + +5) Build du projet +sudo yarn build + +6) Lancement du serveur local de test +sudo yarn start +Par défaut, le port utilisé est le 3000. Si déjà utilisé +sudo PORT=3001 yarn start + +7) Test du composant associé à la branche