@redocly/theme 0.63.0-next.5 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/lib/components/Accordion/variables.js +2 -2
  2. package/lib/components/Catalog/Catalog.js +114 -50
  3. package/lib/components/Catalog/CatalogAvatar.d.ts +5 -0
  4. package/lib/components/Catalog/CatalogAvatar.js +92 -0
  5. package/lib/components/Catalog/CatalogCardView/CatalogCard.js +26 -7
  6. package/lib/components/Catalog/CatalogCardView/CatalogCardView.js +10 -2
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +17 -17
  8. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode.js +4 -20
  9. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNodeContent.d.ts +7 -0
  10. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNodeContent.js +53 -0
  11. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsRootNode.js +3 -17
  12. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistoryButton.js +30 -9
  13. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +16 -3
  14. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem.js +8 -4
  15. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityProperties.js +11 -5
  16. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/DomainsProperty.js +1 -1
  17. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/OwnersProperty.js +1 -1
  18. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  19. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.js +4 -1
  20. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent.js +7 -2
  21. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  22. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +1 -1
  23. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +1 -1
  24. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.d.ts +4 -2
  25. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.js +25 -8
  26. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.d.ts +7 -0
  27. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.js +24 -0
  28. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelHeader.d.ts +6 -0
  29. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelHeader.js +26 -0
  30. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.d.ts +7 -0
  31. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.js +30 -0
  32. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBar.d.ts +13 -0
  33. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBar.js +30 -0
  34. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarControls.d.ts +11 -0
  35. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarControls.js +21 -0
  36. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarFiltersButton.d.ts +7 -0
  37. package/lib/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarFiltersButton.js +25 -0
  38. package/lib/components/Catalog/CatalogPageDescription.js +0 -6
  39. package/lib/components/Catalog/CatalogSelector.d.ts +0 -1
  40. package/lib/components/Catalog/CatalogSelector.js +50 -16
  41. package/lib/components/Catalog/CatalogTableView/CatalogTableView.d.ts +2 -1
  42. package/lib/components/Catalog/CatalogTableView/CatalogTableView.js +20 -5
  43. package/lib/components/Catalog/CatalogTableView/CatalogUserEntityCell.js +2 -23
  44. package/lib/components/Catalog/CatalogTagsWithTooltip.d.ts +2 -1
  45. package/lib/components/Catalog/CatalogTagsWithTooltip.js +14 -6
  46. package/lib/components/Catalog/variables.js +78 -36
  47. package/lib/components/Filter/variables.js +1 -1
  48. package/lib/components/LoadMore/LoadMore.js +1 -0
  49. package/lib/components/PageActions/PageActions.js +1 -2
  50. package/lib/components/UserMenu/UserMenu.js +1 -3
  51. package/lib/core/hooks/index.d.ts +1 -0
  52. package/lib/core/hooks/index.js +1 -0
  53. package/lib/core/hooks/use-is-truncated.d.ts +1 -0
  54. package/lib/core/hooks/use-is-truncated.js +19 -0
  55. package/lib/core/hooks/use-tabs.d.ts +1 -1
  56. package/lib/core/hooks/use-tabs.js +30 -17
  57. package/lib/core/types/l10n.d.ts +1 -1
  58. package/lib/core/utils/custom-catalog-options-casing.d.ts +15 -0
  59. package/lib/core/utils/custom-catalog-options-casing.js +32 -0
  60. package/lib/core/utils/index.d.ts +1 -0
  61. package/lib/core/utils/index.js +1 -0
  62. package/lib/markdoc/components/MarkdocExample/MarkdocExample.js +2 -2
  63. package/lib/markdoc/components/Tabs/Tabs.d.ts +1 -2
  64. package/lib/markdoc/components/Tabs/Tabs.js +9 -22
  65. package/package.json +4 -4
  66. package/src/components/Accordion/variables.ts +2 -2
  67. package/src/components/Catalog/Catalog.tsx +157 -95
  68. package/src/components/Catalog/CatalogAvatar.tsx +68 -0
  69. package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +29 -5
  70. package/src/components/Catalog/CatalogCardView/CatalogCardView.tsx +10 -2
  71. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +17 -17
  72. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode.tsx +4 -21
  73. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNodeContent.tsx +82 -0
  74. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsRootNode.tsx +4 -22
  75. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistoryButton.tsx +39 -10
  76. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +19 -3
  77. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem.tsx +8 -4
  78. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityProperties.tsx +12 -10
  79. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/DomainsProperty.tsx +5 -1
  80. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/OwnersProperty.tsx +1 -0
  81. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  82. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.tsx +6 -2
  83. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent.tsx +10 -2
  84. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  85. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +1 -1
  86. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +1 -1
  87. package/src/components/Catalog/CatalogFilter/CatalogFilterContent.tsx +30 -6
  88. package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.tsx +31 -0
  89. package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelHeader.tsx +34 -0
  90. package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.tsx +40 -0
  91. package/src/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBar.tsx +54 -0
  92. package/src/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarControls.tsx +34 -0
  93. package/src/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBarFiltersButton.tsx +39 -0
  94. package/src/components/Catalog/CatalogPageDescription.tsx +0 -6
  95. package/src/components/Catalog/CatalogSelector.tsx +23 -21
  96. package/src/components/Catalog/CatalogTableView/CatalogTableView.tsx +37 -6
  97. package/src/components/Catalog/CatalogTableView/CatalogUserEntityCell.tsx +2 -26
  98. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +24 -9
  99. package/src/components/Catalog/variables.ts +78 -36
  100. package/src/components/Filter/variables.ts +1 -1
  101. package/src/components/LoadMore/LoadMore.tsx +1 -0
  102. package/src/components/PageActions/PageActions.tsx +1 -2
  103. package/src/components/UserMenu/UserMenu.tsx +1 -3
  104. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +1 -1
  105. package/src/core/hooks/index.ts +1 -0
  106. package/src/core/hooks/use-is-truncated.ts +20 -0
  107. package/src/core/hooks/use-tabs.ts +40 -21
  108. package/src/core/types/l10n.ts +2 -0
  109. package/src/core/utils/custom-catalog-options-casing.ts +29 -0
  110. package/src/core/utils/index.ts +1 -0
  111. package/src/markdoc/components/MarkdocExample/MarkdocExample.tsx +2 -6
  112. package/src/markdoc/components/Tabs/Tabs.tsx +4 -37
@@ -1,14 +1,19 @@
1
- import React, { JSX } from 'react';
1
+ import React, { useState, useMemo, JSX } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import type { CatalogEntityConfig } from '@redocly/config';
5
+ import type { ResolvedFilter } from '@redocly/theme/core/types';
5
6
 
6
7
  import {
7
8
  BffCatalogEntityList,
8
9
  CatalogSwitcherItem,
9
10
  CatalogViewMode,
10
11
  } from '@redocly/theme/core/types';
11
- import { breakpoints } from '@redocly/theme/core/utils';
12
+ import {
13
+ breakpoints,
14
+ customCatalogOptionsCasing,
15
+ isFromToSelectedOptions,
16
+ } from '@redocly/theme/core/utils';
12
17
  import { useThemeHooks } from '@redocly/theme/core/hooks';
13
18
  import { H3 } from '@redocly/theme/components/Typography/H3';
14
19
  import { CatalogFilterContent } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterContent';
@@ -20,9 +25,27 @@ import { FilterInput } from '@redocly/theme/components/Filter/FilterInput';
20
25
  import { CatalogViewModeToggle } from '@redocly/theme/components/Catalog/CatalogViewModeToggle';
21
26
  import { CatalogSortButton } from '@redocly/theme/components/Catalog/CatalogSortButton';
22
27
  import { CatalogEntities } from '@redocly/theme/components/Catalog/CatalogEntities';
28
+ import { CatalogMobileTopBar } from '@redocly/theme/components/Catalog/CatalogMobileTopBar/CatalogMobileTopBar';
29
+ import { CatalogMobileFiltersPanelOverlay } from '@redocly/theme/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay';
23
30
 
24
31
  type CatalogFiltersWithCounts = Record<string, { value: string; count: number }[]>;
25
32
 
33
+ function getActiveFiltersCount(filters: ResolvedFilter[], filterTerm: string): number {
34
+ const filterOptionsCount = filters.reduce((sum, filter) => {
35
+ if (filter.selectedOptions instanceof Set) return sum + filter.selectedOptions.size;
36
+ if (
37
+ isFromToSelectedOptions(filter.selectedOptions) &&
38
+ filter.selectedOptions.from &&
39
+ filter.selectedOptions.to
40
+ ) {
41
+ return sum + 1;
42
+ }
43
+ return sum;
44
+ }, 0);
45
+ const searchTermCount = filterTerm.trim() ? 1 : 0;
46
+ return searchTermCount + filterOptionsCount;
47
+ }
48
+
26
49
  export type CatalogProps = {
27
50
  catalogConfig: CatalogEntityConfig;
28
51
  filters?: CatalogFiltersWithCounts;
@@ -32,22 +55,6 @@ export type CatalogProps = {
32
55
  initialViewMode?: CatalogViewMode;
33
56
  };
34
57
 
35
- const customCatalogOptionsCasing = (str: string): string => {
36
- const trimmedStr = str.trim();
37
- if (!trimmedStr) return trimmedStr;
38
-
39
- const words = trimmedStr.split(/[\s-_]+/);
40
-
41
- return words
42
- .map((word, index) => {
43
- if (index === 0 && word.toLowerCase() === 'api') {
44
- return 'API';
45
- }
46
- return word[0].toUpperCase() + word.slice(1);
47
- })
48
- .join(' ');
49
- };
50
-
51
58
  export function Catalog(props: CatalogProps): JSX.Element {
52
59
  const {
53
60
  catalogConfig,
@@ -86,32 +93,45 @@ export function Catalog(props: CatalogProps): JSX.Element {
86
93
  initialViewMode,
87
94
  });
88
95
 
96
+ const [mobileFiltersPanelOpen, setMobileFiltersPanelOpen] = useState(false);
97
+ const activeFiltersCount = useMemo(
98
+ () => getActiveFiltersCount(filters, searchQuery),
99
+ [filters, searchQuery],
100
+ );
101
+
102
+ const filterContent = (
103
+ <CatalogFilterContent
104
+ catalogSelector={
105
+ <CatalogSelector
106
+ catalogSwitcherItems={catalogSwitcherItems}
107
+ onChange={() => {
108
+ setSearchQuery('');
109
+ setSortOption(null);
110
+ }}
111
+ />
112
+ }
113
+ filters={filters}
114
+ filterTerm={searchQuery}
115
+ filterValuesCasing={customCatalogOptionsCasing}
116
+ onClear={() => {
117
+ setSearchQuery('');
118
+ }}
119
+ searchInput={
120
+ <FilterInput
121
+ value={searchQuery}
122
+ onChange={(updatedTerm) => setSearchQuery(updatedTerm)}
123
+ dataTestId="catalog-filter-search-input"
124
+ />
125
+ }
126
+ />
127
+ );
128
+
89
129
  return (
90
130
  <>
91
131
  <CatalogPageWrapper data-component-name="Catalog/Catalog">
92
132
  <FiltersSidebar
93
133
  collapsed={collapsedSidebar}
94
- header={
95
- collapsedSidebar ? null : (
96
- <CatalogSelector
97
- catalogSwitcherItems={catalogSwitcherItems}
98
- onChange={() => {
99
- setSearchQuery('');
100
- setSortOption(null);
101
- }}
102
- />
103
- )
104
- }
105
- menu={
106
- <CatalogFilterContent
107
- filters={filters}
108
- filterTerm={searchQuery}
109
- filterValuesCasing={customCatalogOptionsCasing}
110
- onClear={() => {
111
- setSearchQuery('');
112
- }}
113
- />
114
- }
134
+ menu={filterContent}
115
135
  footer={
116
136
  <SidebarActions
117
137
  layout={layout}
@@ -122,51 +142,69 @@ export function Catalog(props: CatalogProps): JSX.Element {
122
142
  />
123
143
  }
124
144
  />
125
- <CatalogPageContent>
126
- <CatalogPageDescription>
127
- <CatalogTitleWrapper>
128
- <CatalogTitle data-translation-key={catalogConfig?.titleTranslationKey}>
129
- {catalogConfig?.titleTranslationKey
130
- ? translate(catalogConfig?.titleTranslationKey)
131
- : catalogConfig?.slug}{' '}
132
- </CatalogTitle>
133
- <CounterTag borderless>{entitiesCounter}</CounterTag>
134
- </CatalogTitleWrapper>
135
- <CatalogDescription data-translation-key={catalogConfig?.descriptionTranslationKey}>
136
- {translate(catalogConfig?.descriptionTranslationKey)}
137
- </CatalogDescription>
138
- </CatalogPageDescription>
139
-
140
- <CatalogActionsRow>
141
- <CatalogSearchInputWrapper>
142
- <FilterInput
143
- value={searchQuery}
144
- onChange={(updatedTerm) => setSearchQuery(updatedTerm)}
145
- dataTestId="catalog-search-input"
146
- />
147
- </CatalogSearchInputWrapper>
148
-
149
- <CatalogControlsWrapper>
150
- <CatalogSortButton onSortChange={setSortOption} currentSort={sortOption} />
151
- <CatalogViewModeToggle viewMode={viewMode} onViewModeChange={setViewMode} />
152
- </CatalogControlsWrapper>
153
- </CatalogActionsRow>
154
-
155
- <CatalogEntities
156
- catalogConfig={catalogConfig}
157
- excludedEntities={catalogConfig?.excludes}
158
- filterQuery={filterQuery}
159
- entitiesTypes={entitiesTypes}
145
+ <CatalogContentWrapper>
146
+ <CatalogMobileTopBar
147
+ setMobileFiltersPanelOpen={setMobileFiltersPanelOpen}
148
+ activeFiltersCount={activeFiltersCount}
149
+ setSortOption={setSortOption}
160
150
  sortOption={sortOption}
161
- searchQuery={searchQuery}
162
151
  viewMode={viewMode}
163
- setEntitiesCounter={setEntitiesCounter}
164
- initialEntitiesList={initialEntitiesList}
165
- setSortOption={setSortOption}
166
- handleSortClick={handleSortClick}
167
- isColumnSorted={isColumnSorted}
152
+ setViewMode={setViewMode}
168
153
  />
169
- </CatalogPageContent>
154
+
155
+ {mobileFiltersPanelOpen ? (
156
+ <CatalogMobileFiltersPanelOverlay
157
+ setMobileFiltersPanelOpen={setMobileFiltersPanelOpen}
158
+ filterContent={filterContent}
159
+ />
160
+ ) : null}
161
+
162
+ <CatalogContentScrollWrapper>
163
+ <CatalogPageContentWrapper>
164
+ <CatalogTitleWrapper>
165
+ <CatalogTitle data-translation-key={catalogConfig?.titleTranslationKey}>
166
+ {catalogConfig?.titleTranslationKey
167
+ ? translate(catalogConfig?.titleTranslationKey)
168
+ : catalogConfig?.slug}{' '}
169
+ </CatalogTitle>
170
+ <CounterTag borderless>{entitiesCounter}</CounterTag>
171
+ </CatalogTitleWrapper>
172
+ <CatalogDescription data-translation-key={catalogConfig?.descriptionTranslationKey}>
173
+ {translate(catalogConfig?.descriptionTranslationKey)}
174
+ </CatalogDescription>
175
+
176
+ <CatalogActionsRow>
177
+ <CatalogSearchInputWrapper>
178
+ <FilterInput
179
+ value={searchQuery}
180
+ onChange={(updatedTerm) => setSearchQuery(updatedTerm)}
181
+ dataTestId="catalog-search-input"
182
+ />
183
+ </CatalogSearchInputWrapper>
184
+
185
+ <CatalogControlsWrapper>
186
+ <CatalogSortButton onSortChange={setSortOption} currentSort={sortOption} />
187
+ <CatalogViewModeToggle viewMode={viewMode} onViewModeChange={setViewMode} />
188
+ </CatalogControlsWrapper>
189
+ </CatalogActionsRow>
190
+
191
+ <CatalogEntities
192
+ catalogConfig={catalogConfig}
193
+ excludedEntities={catalogConfig?.excludes}
194
+ filterQuery={filterQuery}
195
+ entitiesTypes={entitiesTypes}
196
+ sortOption={sortOption}
197
+ searchQuery={searchQuery}
198
+ viewMode={viewMode}
199
+ setEntitiesCounter={setEntitiesCounter}
200
+ initialEntitiesList={initialEntitiesList}
201
+ setSortOption={setSortOption}
202
+ handleSortClick={handleSortClick}
203
+ isColumnSorted={isColumnSorted}
204
+ />
205
+ </CatalogPageContentWrapper>
206
+ </CatalogContentScrollWrapper>
207
+ </CatalogContentWrapper>
170
208
  </CatalogPageWrapper>
171
209
  </>
172
210
  );
@@ -175,6 +213,11 @@ export function Catalog(props: CatalogProps): JSX.Element {
175
213
  const CatalogActionsRow = styled.div`
176
214
  display: flex;
177
215
  align-items: center;
216
+ margin-bottom: var(--spacing-xl);
217
+
218
+ @media screen and (max-width: ${breakpoints.medium}) {
219
+ display: none;
220
+ }
178
221
  `;
179
222
 
180
223
  const CatalogControlsWrapper = styled.div`
@@ -184,14 +227,30 @@ const CatalogControlsWrapper = styled.div`
184
227
  gap: var(--catalog-controls-wrapper-gap);
185
228
  `;
186
229
 
187
- const CatalogPageContent = styled.main`
230
+ const CatalogContentWrapper = styled.div`
231
+ flex: 1;
232
+ min-width: 0;
233
+ min-height: 0;
234
+ display: flex;
235
+ flex-direction: column;
236
+ overflow: hidden;
237
+ `;
238
+
239
+ const CatalogContentScrollWrapper = styled.div`
188
240
  flex: 1;
189
- width: var(--catalog-page-content-width-mobile);
190
- margin: var(--catalog-page-content-margin-mobile);
241
+ min-height: 0;
242
+ overflow-y: auto;
243
+ overflow-x: hidden;
244
+ `;
191
245
 
192
- @media screen and (min-width: ${breakpoints.medium}) {
193
- width: var(--catalog-page-content-width-desktop);
194
- padding: var(--catalog-page-padding);
246
+ const CatalogPageContentWrapper = styled.main`
247
+ width: var(--catalog-page-content-width);
248
+ margin: var(--catalog-page-content-margin);
249
+ padding: var(--catalog-page-padding);
250
+ max-width: 1072px;
251
+
252
+ @media screen and (max-width: ${breakpoints.small}) {
253
+ padding: var(--catalog-page-padding-mobile);
195
254
  }
196
255
  `;
197
256
 
@@ -222,8 +281,10 @@ const CatalogDescription = styled.p`
222
281
  const CatalogPageWrapper = styled.div`
223
282
  --sidebar-width: var(--catalog-sidebar-width);
224
283
 
284
+ height: calc(100vh - var(--navbar-height) - var(--banner-height));
225
285
  display: flex;
226
286
  flex-direction: column;
287
+ overflow: hidden;
227
288
 
228
289
  font-weight: var(--catalog-page-wrapper-font-weight);
229
290
  color: var(--catalog-page-wrapper-text-color);
@@ -249,17 +310,13 @@ const CatalogPageWrapper = styled.div`
249
310
  }
250
311
  `;
251
312
 
252
- const CatalogPageDescription = styled.div`
253
- display: var(--catalog-page-description-display-mobile);
254
-
255
- @media screen and (min-width: ${breakpoints.medium}) {
256
- display: var(--catalog-page-description-display-desktop);
257
- }
258
- `;
259
-
260
313
  const FiltersSidebar = styled(Sidebar)`
261
314
  display: var(--catalog-sidebar-display-mobile);
262
315
 
316
+ @media screen and (max-width: ${breakpoints.medium}) {
317
+ display: none;
318
+ }
319
+
263
320
  @media screen and (min-width: ${breakpoints.medium}) {
264
321
  display: var(--catalog-sidebar-display-desktop);
265
322
  }
@@ -271,5 +328,10 @@ const FiltersSidebar = styled(Sidebar)`
271
328
  `;
272
329
 
273
330
  const CatalogSearchInputWrapper = styled.div`
331
+ display: none;
274
332
  width: var(--catalog-search-input-wrapper-width);
333
+
334
+ @media screen and (min-width: ${breakpoints.medium}) {
335
+ display: block;
336
+ }
275
337
  `;
@@ -0,0 +1,68 @@
1
+ import styled, { css } from 'styled-components';
2
+ import React from 'react';
3
+
4
+ const extractInitials = (value: string): string => {
5
+ const trimmed = value.trim();
6
+ if (!trimmed) return '';
7
+
8
+ if (trimmed.split('-').length > 1) {
9
+ return trimmed
10
+ .split('-')
11
+ .filter((part) => part.length > 0)
12
+ .map((part) => part[0])
13
+ .join('')
14
+ .toUpperCase()
15
+ .slice(0, 2);
16
+ }
17
+
18
+ const initials = trimmed.split(' ').map((n) => n[0]);
19
+ if (initials.length === 1) {
20
+ return initials[0];
21
+ }
22
+
23
+ return initials[0] + initials[initials.length - 1];
24
+ };
25
+
26
+ export function CatalogAvatar({
27
+ value,
28
+ size,
29
+ }: {
30
+ value: string;
31
+ size: 'small' | 'medium' | 'large';
32
+ }) {
33
+ return (
34
+ <CatalogAvatarWrapper data-component-name="Catalog/CatalogAvatar" size={size}>
35
+ {extractInitials(value)}
36
+ </CatalogAvatarWrapper>
37
+ );
38
+ }
39
+
40
+ const CatalogAvatarWrapper = styled.div<{ size: 'small' | 'medium' | 'large' }>`
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ border-radius: 50%;
45
+ background-color: var(--catalog-avatar-bg-color);
46
+ flex-shrink: 0;
47
+ font-weight: var(--catalog-avatar-font-weight);
48
+ line-height: var(--catalog-avatar-line-height);
49
+ ${({ size }) => SIZES[size]};
50
+ `;
51
+
52
+ const SIZES = {
53
+ small: css`
54
+ width: var(--catalog-avatar-small-size);
55
+ height: var(--catalog-avatar-small-size);
56
+ font-size: var(--catalog-avatar-font-size-small);
57
+ `,
58
+ medium: css`
59
+ width: var(--catalog-avatar-medium-size);
60
+ height: var(--catalog-avatar-medium-size);
61
+ font-size: var(--catalog-avatar-font-size-medium);
62
+ `,
63
+ large: css`
64
+ width: var(--catalog-avatar-large-size);
65
+ height: var(--catalog-avatar-large-size);
66
+ font-size: var(--catalog-avatar-font-size-large);
67
+ `,
68
+ };
@@ -46,7 +46,9 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
46
46
  <CardTitle>
47
47
  <CatalogHighlight>{entity.title}</CatalogHighlight>
48
48
  </CardTitle>
49
- <CatalogHighlight>{entity.summary ?? ''}</CatalogHighlight>
49
+ <CardSummary>
50
+ <CatalogHighlight>{entity.summary ?? ''}</CatalogHighlight>
51
+ </CardSummary>
50
52
  </CardDescription>
51
53
 
52
54
  <CardMetadataSection>
@@ -54,6 +56,7 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
54
56
  <MetadataLabel>{translate('catalog.metadata.domains', 'Domains:')}</MetadataLabel>
55
57
  <CatalogTagsWithTooltip
56
58
  items={entity.domains?.map((domain) => domain.title) || []}
59
+ showPlaceholder={true}
57
60
  tagProps={{
58
61
  style: {
59
62
  fontSize: 'var(--catalog-card-font-size)',
@@ -61,6 +64,7 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
61
64
  },
62
65
  icon: <GraphIcon color="var(--color-green-8)" />,
63
66
  textTransform: 'none',
67
+ variant: 'outline',
64
68
  }}
65
69
  />
66
70
  </MetadataRow>
@@ -69,13 +73,17 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
69
73
  <MetadataLabel>{translate('catalog.metadata.owners', 'Owners:')}</MetadataLabel>
70
74
  <CatalogTagsWithTooltip
71
75
  items={entity.owners?.map((owner) => owner.key) || []}
76
+ showPlaceholder={true}
77
+ showAvatars={true}
72
78
  tagProps={{
73
79
  style: {
74
80
  fontSize: 'var(--catalog-card-font-size)',
75
81
  backgroundColor: 'var(--catalog-card-icon-bg-color)',
76
82
  borderRadius: 'var(--border-radius-xl)',
83
+ paddingLeft: 'var(--catalog-card-metadata-owner-tag-left-padding)',
77
84
  },
78
85
  textTransform: 'none',
86
+ variant: 'outline',
79
87
  }}
80
88
  />
81
89
  </MetadataRow>
@@ -84,7 +92,7 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
84
92
 
85
93
  <Divider />
86
94
 
87
- <CardFooter>
95
+ <CardFooter hasTags={!!entity.tags?.length}>
88
96
  <CatalogTagsWithTooltip
89
97
  items={entity.tags || []}
90
98
  tagProps={{
@@ -169,15 +177,16 @@ const ArrowCircle = styled.div`
169
177
  `;
170
178
 
171
179
  const CardContent = styled.div`
180
+ min-width: 0;
172
181
  padding: var(--catalog-card-padding-vertical) var(--catalog-card-padding-horizontal);
173
182
  height: 100%;
174
183
  `;
175
184
 
176
- const CardFooter = styled.div`
185
+ const CardFooter = styled.div<{ hasTags: boolean }>`
177
186
  height: var(--catalog-card-footer-height);
178
187
  display: flex;
179
188
  align-items: center;
180
- justify-content: space-between;
189
+ justify-content: ${({ hasTags }) => (hasTags ? 'space-between' : 'flex-end')};
181
190
  padding: var(--catalog-card-padding-vertical) var(--catalog-card-padding-horizontal);
182
191
  `;
183
192
 
@@ -188,6 +197,8 @@ const Divider = styled.div`
188
197
  `;
189
198
 
190
199
  const CatalogCardWrapper = styled.div`
200
+ min-width: 0;
201
+ max-width: 100%;
191
202
  color: var(--catalog-card-text-color);
192
203
  font-size: var(--catalog-card-font-size);
193
204
  font-weight: var(--catalog-card-font-weight);
@@ -221,7 +232,20 @@ const CardTitle = styled.div`
221
232
  font-size: var(--catalog-card-title-font-size);
222
233
  font-weight: var(--catalog-card-title-font-weight);
223
234
  line-height: var(--catalog-card-title-line-height);
224
- margin: 0;
235
+ height: var(--catalog-card-title-height);
236
+ display: var(--catalog-card-title-display);
237
+ align-items: var(--catalog-card-title-align-items);
238
+ margin-bottom: var(--catalog-card-title-margin-bottom);
239
+ `;
240
+
241
+ const CardSummary = styled.div`
242
+ min-width: 0;
243
+ font-size: var(--catalog-card-summary-font-size);
244
+ font-weight: var(--catalog-card-summary-font-weight);
245
+ line-height: var(--catalog-card-summary-line-height);
246
+ white-space: nowrap;
247
+ overflow: hidden;
248
+ text-overflow: ellipsis;
225
249
  `;
226
250
 
227
251
  const CardDescription = styled.div`
@@ -4,6 +4,7 @@ import styled from 'styled-components';
4
4
  import type { CatalogEntityConfig } from '@redocly/config';
5
5
  import type { BffCatalogEntity } from '@redocly/theme/core/types';
6
6
 
7
+ import { breakpoints } from '@redocly/theme/core/utils';
7
8
  import { CatalogCard } from '@redocly/theme/components/Catalog/CatalogCardView/CatalogCard';
8
9
 
9
10
  export type CatalogCardViewProps = {
@@ -23,8 +24,15 @@ export function CatalogCardView({ entities, catalogConfig }: CatalogCardViewProp
23
24
 
24
25
  const CatalogCardsWrapper = styled.div`
25
26
  display: grid;
26
- grid-template-columns: repeat(auto-fill, minmax(var(--catalog-card-min-width), 1fr));
27
27
  gap: var(--catalog-cards-group-gap);
28
28
  margin-bottom: var(--spacing-xl);
29
- margin-top: var(--spacing-xl);
29
+ overflow-x: auto;
30
+
31
+ @media screen and (min-width: ${breakpoints.small}) {
32
+ grid-template-columns: repeat(2, 1fr);
33
+ }
34
+
35
+ @media screen and (max-width: ${breakpoints.medium}) {
36
+ margin-top: 0;
37
+ }
30
38
  `;
@@ -162,6 +162,22 @@ export function CatalogEntity({
162
162
  }
163
163
 
164
164
  const CatalogPageWrapper = styled.div`
165
+ max-width: var(--catalog-entity-max-width);
166
+ margin: var(--catalog-entity-margin);
167
+ padding: var(--catalog-entity-padding);
168
+
169
+ @media screen and (max-width: ${breakpoints.large}) {
170
+ padding: var(--catalog-entity-padding-large);
171
+ }
172
+
173
+ @media screen and (max-width: ${breakpoints.medium}) {
174
+ padding: var(--catalog-entity-padding-medium);
175
+ }
176
+
177
+ @media screen and (max-width: ${breakpoints.small}) {
178
+ padding: var(--catalog-entity-padding-small);
179
+ }
180
+
165
181
  --sidebar-width: var(--catalog-sidebar-width);
166
182
  --link-color-primary: var(--breadcrumbs-text-color);
167
183
 
@@ -175,33 +191,17 @@ const CatalogPageWrapper = styled.div`
175
191
  font-family: var(--font-family-base);
176
192
  line-height: var(--line-height-base);
177
193
 
178
- hr {
179
- border: 0;
180
- width: calc(100% + 48px);
181
- margin: auto -24px 0 -24px;
182
- border-top: 1px solid var(--border-color-primary);
183
- }
184
194
  a:not([role='button']) {
185
195
  text-decoration: none;
186
196
  color: var(--link-color-primary);
187
197
  font-weight: var(--link-font-weight);
188
198
  }
189
-
190
- @media screen and (min-width: ${breakpoints.medium}) {
191
- flex-direction: row;
192
- padding: 0;
193
- }
194
199
  `;
195
200
 
196
201
  const CatalogPageContent = styled.main`
197
202
  flex: 1;
198
- width: 90%;
203
+ width: 100%;
199
204
  margin: 0 auto;
200
-
201
- @media screen and (min-width: ${breakpoints.medium}) {
202
- width: 100%;
203
- padding: var(--catalog-page-padding);
204
- }
205
205
  `;
206
206
 
207
207
  const CatalogEntityPageWrapper = styled.div`
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
2
  import { Handle, Position } from '@xyflow/react';
3
- import styled from 'styled-components';
4
3
 
5
- import { CatalogEntityIcon } from '@redocly/theme/components/Catalog/CatalogEntityIcon';
6
4
  import { Link } from '@redocly/theme/components/Link/Link';
7
5
  import { GraphHandleType } from '@redocly/theme/core/constants/catalog';
6
+ import { CatalogEntityRelationsNodeContent } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNodeContent';
8
7
 
9
8
  export type CatalogEntityRelationsLinkedNodeProps = {
10
9
  label: string;
@@ -20,29 +19,13 @@ export function CatalogEntityRelationsLinkedNode({
20
19
  to,
21
20
  }: CatalogEntityRelationsLinkedNodeProps): React.ReactElement {
22
21
  return (
23
- <RelationsLinkedNodeWrapper
22
+ <Link
24
23
  className={className}
25
24
  to={to}
26
25
  data-component-name="Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode"
27
26
  >
27
+ <CatalogEntityRelationsNodeContent entityType={entityType} label={label} isRoot={false} />
28
28
  <Handle type={GraphHandleType.Target} position={Position.Top} id={GraphHandleType.Target} />
29
- <CatalogEntityIcon entityType={entityType} defaultColor={false} />
30
- <div>{label}</div>
31
- </RelationsLinkedNodeWrapper>
29
+ </Link>
32
30
  );
33
31
  }
34
-
35
- const RelationsLinkedNodeWrapper = styled(Link)`
36
- display: flex;
37
- align-items: center;
38
- gap: var(--catalog-entity-relations-node-gap);
39
- padding: var(--catalog-entity-relations-node-padding);
40
- border-radius: var(--catalog-entity-relations-node-border-radius);
41
- font-style: normal;
42
- font-weight: var(--catalog-entity-relations-node-font-weight);
43
- background: var(--catalog-entity-relations-node-bg-color);
44
- color: var(--catalog-entity-relations-node-text-color);
45
- border: var(--catalog-entity-relations-node-border-width)
46
- var(--catalog-entity-relations-node-border-style)
47
- var(--catalog-entity-relations-node-border-color);
48
- `;