import { SideNavigation as SideNavigationPrimitive, SideNavigationItemProvider as ItemProviderPrimitive, useCollapsibleContext, useSideNavigationContext, useSideNavigationItem, useSideNavigationItemContext, type UseSideNavigationItemProps, type SideNavigationProviderProps as SideNavigationProviderPrimitiveProps, } from "@seed-design/react-side-navigation"; import { sideNavigation, type SideNavigationVariantProps, } from "@seed-design/css/recipes/side-navigation"; import { sideNavigationInset, type SideNavigationInsetVariantProps, } from "@seed-design/css/recipes/side-navigation-inset"; import { sideNavigationMenuItem, type SideNavigationMenuItemVariantProps, } from "@seed-design/css/recipes/side-navigation-menu-item"; import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive"; import { createSlotRecipeContext } from "../../utils/createSlotRecipeContext"; import { createRecipeContext } from "../../utils/createRecipeContext"; import { InternalIcon, type InternalIconProps } from "../private/Icon"; import { createWithStateProps } from "../../utils/createWithStateProps"; import { mediaQueries } from "@seed-design/css/breakpoints"; import { composeRefs } from "@radix-ui/react-compose-refs"; import React from "react"; import clsx from "clsx"; import { dataAttr } from "@seed-design/dom-utils"; const { withProvider, withContext, useClassNames } = createSlotRecipeContext(sideNavigation); const { ClassNamesProvider: ItemClassNamesProvider, withContext: withItemContext } = createSlotRecipeContext(sideNavigationMenuItem); const { withContext: withInsetContext } = createRecipeContext(sideNavigationInset); const withSideNavigationStateProps = createWithStateProps([useSideNavigationContext]); const withItemStateProps = createWithStateProps([ useSideNavigationContext, { useContext: useSideNavigationItemContext, strict: false }, ]); const withItemSlotCollapsibleStateProps = createWithStateProps([ useSideNavigationContext, { useContext: useSideNavigationItemContext, strict: false }, { useContext: useCollapsibleContext, strict: false }, ]); //////////////////////////////////////////////////////////////////////////////////// function ResponsiveCollapseEffect() { const { setCollapsed } = useSideNavigationContext(); React.useEffect(() => { const mql = window.matchMedia(mediaQueries.lg); const handler = (e: MediaQueryListEvent) => { setCollapsed(!e.matches); }; mql.addEventListener("change", handler); return () => mql.removeEventListener("change", handler); }, [setCollapsed]); return null; } export interface SideNavigationProviderProps extends SideNavigationProviderPrimitiveProps {} export function SideNavigationProvider(props: SideNavigationProviderProps) { return ( {props.children} ); } SideNavigationProvider.displayName = "SideNavigationProvider"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationRootProps extends SideNavigationVariantProps, SideNavigationPrimitive.RootProps {} export const SideNavigationRoot = withProvider( SideNavigationPrimitive.Root, "root", ); SideNavigationRoot.displayName = "SideNavigationRoot"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationHeaderProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationHeader = withContext( withSideNavigationStateProps(Primitive.div), "header", ); SideNavigationHeader.displayName = "SideNavigationHeader"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationContentProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationContent = React.forwardRef( ({ className, ...props }, forwardedRef) => { const classNames = useClassNames(); const { stateProps } = useSideNavigationContext(); const ref = React.useRef(null); const [scrolled, setScrolled] = React.useState(false); React.useEffect(() => { const element = ref.current; if (!element) return; const check = () => setScrolled(element.scrollTop > 0); check(); element.addEventListener("scroll", check); const observer = new ResizeObserver(check); observer.observe(element); return () => { element.removeEventListener("scroll", check); observer.disconnect(); }; }, []); return ( ); }, ); SideNavigationContent.displayName = "SideNavigationContent"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationFooterProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationFooter = withContext( withSideNavigationStateProps(Primitive.div), "footer", ); SideNavigationFooter.displayName = "SideNavigationFooter"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationGroupProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationGroup = withContext( withSideNavigationStateProps(Primitive.div), "group", ); SideNavigationGroup.displayName = "SideNavigationGroup"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationGroupLabelProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationGroupLabel = withContext( withSideNavigationStateProps(Primitive.div), "groupLabel", ); SideNavigationGroupLabel.displayName = "SideNavigationGroupLabel"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemProps extends SideNavigationMenuItemVariantProps, UseSideNavigationItemProps, PrimitiveProps, React.HTMLAttributes {} export const SideNavigationItem = React.forwardRef( ({ current, disabled, className, ...props }, ref) => { const [variantProps, restProps] = sideNavigationMenuItem.splitVariantProps(props); const classNames = sideNavigationMenuItem(variantProps); const { stateProps: sideNavStateProps } = useSideNavigationContext(); const api = useSideNavigationItem({ current, disabled }); return ( ); }, ); SideNavigationItem.displayName = "SideNavigationItem"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemLabelProps extends PrimitiveProps, React.HTMLAttributes {} export const SideNavigationItemLabel = withItemContext< HTMLSpanElement, SideNavigationItemLabelProps >(withItemStateProps(Primitive.span), "label"); SideNavigationItemLabel.displayName = "SideNavigationItemLabel"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemPrefixIconProps extends InternalIconProps {} export const SideNavigationItemPrefixIcon = withItemContext< SVGSVGElement, SideNavigationItemPrefixIconProps >(withItemStateProps(InternalIcon), "prefixIcon"); //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemSuffixIconProps extends InternalIconProps {} export const SideNavigationItemSuffixIcon = withItemContext< SVGSVGElement, SideNavigationItemSuffixIconProps >(withItemSlotCollapsibleStateProps(InternalIcon), "suffixIcon"); //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemCollapsibleRootProps extends SideNavigationMenuItemVariantProps, SideNavigationPrimitive.ItemCollapsibleRootProps {} export const SideNavigationItemCollapsibleRoot = React.forwardRef< HTMLDivElement, SideNavigationItemCollapsibleRootProps >(({ children, ...props }, ref) => { const [variantProps, restProps] = sideNavigationMenuItem.splitVariantProps(props); const classNames = sideNavigationMenuItem(variantProps); return ( {children} ); }); SideNavigationItemCollapsibleRoot.displayName = "SideNavigationItemCollapsibleRoot"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemCollapsibleTriggerProps extends UseSideNavigationItemProps, SideNavigationPrimitive.ItemCollapsibleTriggerProps {} export const SideNavigationItemCollapsibleTrigger = React.forwardRef< HTMLButtonElement, SideNavigationItemCollapsibleTriggerProps >(({ current, disabled, className, ...props }, ref) => { const classNames = sideNavigationMenuItem(); const { stateProps: sideNavStateProps } = useSideNavigationContext(); const api = useSideNavigationItem({ current, disabled }); return ( ); }); SideNavigationItemCollapsibleTrigger.displayName = "SideNavigationItemCollapsibleTrigger"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationItemCollapsibleContentProps extends SideNavigationPrimitive.ItemCollapsibleContentProps {} export const SideNavigationItemCollapsibleContent = withItemContext< HTMLDivElement, SideNavigationItemCollapsibleContentProps >(SideNavigationPrimitive.ItemCollapsibleContent, "panel"); SideNavigationItemCollapsibleContent.displayName = "SideNavigationItemCollapsibleContent"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationTriggerProps extends SideNavigationPrimitive.TriggerProps {} export const SideNavigationTrigger = withContext( SideNavigationPrimitive.Trigger, "trigger", ); SideNavigationTrigger.displayName = "SideNavigationTrigger"; //////////////////////////////////////////////////////////////////////////////////// export interface SideNavigationInsetProps extends SideNavigationInsetVariantProps, PrimitiveProps, React.HTMLAttributes {} export const SideNavigationInset = withInsetContext( Primitive.div, );