import React, { useState, useMemo, JSX } from 'react'; import styled from 'styled-components'; import type { CatalogEntityConfig } from '@redocly/config'; import type { ResolvedFilter } from '@redocly/theme/core/types'; import { BffCatalogEntityList, CatalogSwitcherItem, CatalogViewMode, } from '@redocly/theme/core/types'; import { breakpoints, customCatalogOptionsCasing, isFromToSelectedOptions, } from '@redocly/theme/core/utils'; import { useThemeHooks } from '@redocly/theme/core/hooks'; import { H3 } from '@redocly/theme/components/Typography/H3'; import { CatalogFilterContent } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterContent'; import { Sidebar, SidebarHeader } from '@redocly/theme/components/Sidebar/Sidebar'; import { CatalogSelector } from '@redocly/theme/components/Catalog/CatalogSelector'; import { SidebarActions } from '@redocly/theme/components/SidebarActions/SidebarActions'; import { CounterTag } from '@redocly/theme/components/Tags/CounterTag'; import { FilterInput } from '@redocly/theme/components/Filter/FilterInput'; import { CatalogEntities } from '@redocly/theme/components/Catalog/CatalogEntities'; import { CatalogMobileTopBar } from '@redocly/theme/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBar'; import { CatalogMobileFiltersPanelOverlay } from '@redocly/theme/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay'; import { CatalogActionsRow } from '@redocly/theme/components/Catalog/CatalogActionsRow'; type CatalogFiltersWithCounts = Record; function getActiveFiltersCount(filters: ResolvedFilter[], filterTerm: string): number { const filterOptionsCount = filters.reduce((sum, filter) => { if (filter.selectedOptions instanceof Set) return sum + filter.selectedOptions.size; if ( isFromToSelectedOptions(filter.selectedOptions) && filter.selectedOptions.from && filter.selectedOptions.to ) { return sum + 1; } return sum; }, 0); const searchTermCount = filterTerm.trim() ? 1 : 0; return searchTermCount + filterOptionsCount; } export type CatalogProps = { catalogConfig: CatalogEntityConfig; filters?: CatalogFiltersWithCounts; entitiesTypes: string[]; initialEntitiesList?: BffCatalogEntityList; catalogSwitcherItems: CatalogSwitcherItem[]; initialViewMode?: CatalogViewMode; }; export function Catalog(props: CatalogProps): JSX.Element { const { catalogConfig, filters: serverFilters, entitiesTypes, initialEntitiesList, catalogSwitcherItems, initialViewMode, } = props; const { useTranslate, useCatalog } = useThemeHooks(); const { translate } = useTranslate(); const { filters, searchQuery, setSearchQuery, filterQuery, sortOption, setSortOption, handleSortClick, isColumnSorted, viewMode, setViewMode, entitiesCounter, setEntitiesCounter, onChangeViewClick, onChangeCollapseSidebarClick, layout, collapsedSidebar, } = useCatalog({ config: catalogConfig, entitiesCounterInitial: initialEntitiesList?.page.total || 0, serverFilters, initialViewMode, }); const [mobileFiltersPanelOpen, setMobileFiltersPanelOpen] = useState(false); const activeFiltersCount = useMemo( () => getActiveFiltersCount(filters, searchQuery), [filters, searchQuery], ); const filterContent = ( { setSearchQuery(''); setSortOption(null); }} /> } filters={filters} filterTerm={searchQuery} filterValuesCasing={customCatalogOptionsCasing} onClear={() => { setSearchQuery(''); }} searchInput={ setSearchQuery(updatedTerm)} dataTestId="catalog-filter-search-input" /> } /> ); return ( <> } /> {mobileFiltersPanelOpen ? ( ) : null} {catalogConfig?.titleTranslationKey ? translate(catalogConfig?.titleTranslationKey) : catalogConfig?.slug}{' '} {entitiesCounter} {translate(catalogConfig?.descriptionTranslationKey)} ); } const CatalogContentWrapper = styled.div` flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; `; const CatalogContentScrollWrapper = styled.div` flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; `; const CatalogPageContentWrapper = styled.main` width: var(--catalog-page-content-width); margin: var(--catalog-page-content-margin); padding: var(--catalog-page-padding); max-width: 1072px; @media screen and (max-width: ${breakpoints.small}) { padding: var(--catalog-page-padding-mobile); } `; const CatalogTitleWrapper = styled.div` display: flex; align-items: center; color: var(--catalog-title-text-color); font-weight: var(--catalog-title-font-weight) !important; font-size: var(--catalog-title-font-size); `; const CatalogTitle = styled(H3)` color: var(--catalog-title-text-color); font-weight: var(--catalog-title-font-weight) !important; font-size: var(--catalog-title-font-size); margin: var(--catalog-title-margin); margin-right: var(--catalog-title-spacing-right); `; const CatalogDescription = styled.p` color: var(--catalog-description-text-color); font-weight: var(--catalog-description-font-weight); font-size: var(--catalog-description-font-size); margin: var(--catalog-description-margin); line-height: var(--catalog-description-line-height); `; const CatalogPageWrapper = styled.div` --sidebar-width: var(--catalog-sidebar-width); height: calc(100vh - var(--navbar-height) - var(--banner-height)); display: flex; flex-direction: column; overflow: hidden; font-weight: var(--catalog-page-wrapper-font-weight); color: var(--catalog-page-wrapper-text-color); font-size: var(--catalog-page-wrapper-font-size); font-family: var(--catalog-page-wrapper-font-family); line-height: var(--catalog-page-wrapper-line-height); hr { border: 0; width: var(--catalog-hr-width); margin: var(--catalog-hr-margin); border-top: var(--catalog-hr-border-width) solid var(--catalog-hr-border-color); } a:not([role='button']) { text-decoration: var(--catalog-link-text-decoration); color: var(--catalog-link-color); font-weight: var(--catalog-link-font-weight); } @media screen and (min-width: ${breakpoints.medium}) { flex-direction: row; padding: 0; } `; const FiltersSidebar = styled(Sidebar)` display: var(--catalog-sidebar-display-mobile); @media screen and (max-width: ${breakpoints.medium}) { display: none; } @media screen and (min-width: ${breakpoints.medium}) { display: var(--catalog-sidebar-display-desktop); } --menu-container-padding-top: var(--catalog-sidebar-menu-container-padding-top); ${SidebarHeader} { border: none; } `;