import React, { useState, useEffect, useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { useLocation } from 'react-router-dom'; import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon'; import { useThemeHooks } from '@redocly/theme/core/hooks'; import { MenuContainer } from '@redocly/theme/components/Menu/MenuContainer'; import { transformRevisionsToVersionHistory } from '@redocly/theme/core/utils'; import { DEFAULT_LOCALE_PLACEHOLDER } from '@redocly/theme/core/constants'; import { CatalogEntityVersionItem } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem'; import { RecentlyViewedIcon } from '@redocly/theme/icons/RecentlyViewedIcon/RecentlyViewedIcon'; export type CatalogHistorySidebarProps = { entityKey: string; revision?: string; version?: string | null; className?: string; }; export function CatalogEntityHistorySidebar({ entityKey, revision, version, className, }: CatalogHistorySidebarProps): React.ReactElement | null { const [isOpen, setIsOpen] = useState(false); const location = useLocation(); const basePath = location.pathname; const { useTranslate, useFetchCatalogEntityRevisions, useL10nConfig } = useThemeHooks(); const { translate } = useTranslate(); const { currentLocale } = useL10nConfig(); const { items: revisions } = useFetchCatalogEntityRevisions({ entityKey }); const normalizedLocale = currentLocale && currentLocale !== DEFAULT_LOCALE_PLACEHOLDER ? currentLocale : 'en-US'; const versionHistoryItems = useMemo( () => transformRevisionsToVersionHistory({ revisions, currentRevisionDate: revision, currentVersion: version, locale: normalizedLocale, }), [revisions, revision, version, normalizedLocale], ); const activeRevisionLabel = useMemo(() => { const itemWithActiveRevision = versionHistoryItems.find((item) => item.revisions?.some((revision) => revision.isActive), ); const activeRevision = itemWithActiveRevision?.revisions?.find((revision) => revision.isActive); return activeRevision?.name; }, [versionHistoryItems]); const [expandedVersions, setExpandedVersions] = useState>(new Set()); useEffect(() => { setExpandedVersions( new Set(versionHistoryItems.filter((item) => item.isExpanded).map((item) => item.version)), ); }, [versionHistoryItems]); const handleOpenSidebar = useCallback(() => { setIsOpen(true); }, []); useEffect(() => { window.addEventListener('portal:sidebar:open-version-history', handleOpenSidebar); return () => { window.removeEventListener('portal:sidebar:open-version-history', handleOpenSidebar); }; }, [handleOpenSidebar]); const handleClose = (): void => { window.dispatchEvent( new CustomEvent('portal:sidebar:close-version-history', { detail: { version, revision: activeRevisionLabel }, }), ); setIsOpen(false); }; const toggleVersion = (version: string): void => { setExpandedVersions((prev) => { const newSet = new Set(prev); if (newSet.has(version)) { newSet.delete(version); } else { newSet.add(version); } return newSet; }); }; if (!isOpen) { return null; } return ( {translate('catalog.history.sidebar.title', 'Version history')} {versionHistoryItems.map((group) => ( ))} ); } const SidebarOverlay = styled.div` position: fixed; top: calc(var(--navbar-height) + var(--banner-height)); left: 0; width: var(--sidebar-width); height: calc(100vh - var(--navbar-height) - var(--banner-height)); background-color: var(--catalog-history-sidebar-bg-color); border-right: 1px solid var(--catalog-history-sidebar-border-color); display: flex; flex-direction: column; z-index: calc(var(--z-index-raised) - 1); overflow: hidden; `; const SidebarHeader = styled.div` display: flex; align-items: center; justify-content: space-between; padding: var(--catalog-history-sidebar-header-padding); border-bottom: 1px solid var(--catalog-history-sidebar-border-color); flex-shrink: 0; `; const HeaderTitle = styled.h2` margin: 0; font-family: var(--catalog-history-sidebar-header-font-family); font-size: var(--catalog-history-sidebar-header-font-size); font-weight: var(--catalog-history-sidebar-header-font-weight); line-height: var(--catalog-history-sidebar-header-line-height); letter-spacing: var(--catalog-history-sidebar-header-letter-spacing); color: var(--catalog-history-sidebar-header-color); `; const CloseButton = styled.button` all: unset; display: flex; align-items: center; justify-content: center; width: var(--catalog-history-sidebar-close-button-size); height: var(--catalog-history-sidebar-close-button-size); cursor: pointer; border-radius: var(--catalog-history-sidebar-close-button-border-radius); transition: background-color 0.2s ease; &:hover { background-color: var(--catalog-history-sidebar-close-button-bg-color-hover); } `; const VersionList = styled.ul` list-style: none; margin: 0; padding: var(--catalog-history-sidebar-content-padding); margin-top: var(--catalog-history-sidebar-content-margin-top); `; const HistoryIcon = styled(RecentlyViewedIcon)` width: var(--catalog-history-icon-size); height: var(--catalog-history-icon-size); flex-shrink: 0; `; const HeaderWrapper = styled.div` display: flex; align-items: center; gap: var(--spacing-xs); `;