diff --git a/src/theme/DocSidebarItem/Category/index.tsx b/src/theme/DocSidebarItem/Category/index.tsx new file mode 100644 index 000000000..1dd346ece --- /dev/null +++ b/src/theme/DocSidebarItem/Category/index.tsx @@ -0,0 +1,210 @@ +import React, { type ComponentProps, useEffect, useMemo } from 'react'; +import clsx from 'clsx'; +import { + ThemeClassNames, + useThemeConfig, + usePrevious, + Collapsible, + useCollapsible, +} from '@docusaurus/theme-common'; +import { + isActiveSidebarItem, + findFirstCategoryLink, + useDocSidebarItemsExpandedState, + isSamePath, +} from '@docusaurus/theme-common/internal'; +import Link from '@docusaurus/Link'; +import { translate } from '@docusaurus/Translate'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import type { Props } from '@theme/DocSidebarItem/Category'; +import { useHistory } from '@docusaurus/router'; + +// If we navigate to a category and it becomes active, it should automatically +// expand itself +function useAutoExpandActiveCategory({ + isActive, + collapsed, + updateCollapsed, +}: { + isActive: boolean; + collapsed: boolean; + updateCollapsed: (b: boolean) => void; +}) { + const wasActive = usePrevious(isActive); + useEffect(() => { + const justBecameActive = isActive && !wasActive; + if (justBecameActive && collapsed) { + updateCollapsed(false); + } + }, [isActive, wasActive, collapsed, updateCollapsed]); +} + +/** + * When a collapsible category has no link, we still link it to its first child + * during SSR as a temporary fallback. This allows to be able to navigate inside + * the category even when JS fails to load, is delayed or simply disabled + * React hydration becomes an optional progressive enhancement + * see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188 + * see https://github.com/facebook/docusaurus/issues/3030 + */ +function useCategoryHrefWithSSRFallback( + item: Props['item'] +): string | undefined { + const isBrowser = useIsBrowser(); + return useMemo(() => { + if (item.href) { + return item.href; + } + // In these cases, it's not necessary to render a fallback + // We skip the "findFirstCategoryLink" computation + if (isBrowser || !item.collapsible) { + return undefined; + } + return findFirstCategoryLink(item); + }, [item, isBrowser]); +} + +function CollapseButton({ + categoryLabel, + onClick, +}: { + categoryLabel: string; + onClick: ComponentProps<'button'>['onClick']; +}) { + return ( +