@redocly/theme 0.19.0 → 0.19.2

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.
@@ -27,10 +27,13 @@ exports.useCatalog = void 0;
27
27
  const React = __importStar(require("react"));
28
28
  const react_router_dom_1 = require("react-router-dom");
29
29
  const telemetry_1 = require("../../mocks/telemetry");
30
+ const usePageData_1 = require("../../mocks/hooks/usePageData");
30
31
  const utils_1 = require("../../utils");
31
32
  function useCatalog(items, config) {
33
+ var _a;
32
34
  const location = (0, react_router_dom_1.useLocation)();
33
35
  const navigate = (0, react_router_dom_1.useNavigate)();
36
+ const pageData = (0, usePageData_1.usePageData)();
34
37
  const searchParams = useSearchParams(location);
35
38
  const [filtersState, setFiltersState] = React.useState(() => {
36
39
  var _a;
@@ -47,7 +50,8 @@ function useCatalog(items, config) {
47
50
  });
48
51
  const [filterTerm, setFilterTerm] = React.useState(() => searchParams.get('filter') || '');
49
52
  const filtersWithOptions = React.useMemo(() => collectFilterOptions(items, config.filters), [items, config.filters]);
50
- const normalizedItems = React.useMemo(() => normalizeItems(items, config), [items, config]);
53
+ const customFields = (_a = pageData === null || pageData === void 0 ? void 0 : pageData.props) === null || _a === void 0 ? void 0 : _a.customFields;
54
+ const normalizedItems = React.useMemo(() => normalizeItems(items, config, customFields || {}), [items, config, customFields]);
51
55
  const toggleOption = React.useCallback((filterIdx, option) => {
52
56
  setFiltersState((prev) => {
53
57
  const newFilterOptions = prev[filterIdx] ? prev[filterIdx] : new Set();
@@ -178,7 +182,7 @@ function groupByFirstFilter(filters, filteredItems) {
178
182
  function useSearchParams(location) {
179
183
  return React.useMemo(() => new URLSearchParams(location.search), [location.search]);
180
184
  }
181
- function normalizeItems(items, config) {
185
+ function normalizeItems(items, config, customFields) {
182
186
  // because of RBAC some versions can be missing so we need to pick up other default version
183
187
  const hasDeafultVersion = {};
184
188
  for (const item of items) {
@@ -203,9 +207,10 @@ function normalizeItems(items, config) {
203
207
  return items.map((item) => {
204
208
  var _a, _b, _c;
205
209
  const metadata = item.metadata || {};
210
+ const apiCustomFields = customFields[item.fsPath || ''] || {};
206
211
  const link = item.link || ((_a = (0, utils_1.findDeepFirst)(item.items || [], (i) => 'link' in i && !!i.link)) === null || _a === void 0 ? void 0 : _a.link);
207
212
  const firstSidebarItem = (_b = item.sidebar) === null || _b === void 0 ? void 0 : _b[0];
208
- return Object.assign(Object.assign({}, metadata), { publishedAt: metadata.publishedAt || metadata.createdAt, title: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.title) || item.label || 'Untitled', description: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.description), link: (_c = (0, utils_1.withoutHash)(link)) !== null && _c !== void 0 ? _c : '#', docsLink: (0, utils_1.withoutHash)(firstSidebarItem === null || firstSidebarItem === void 0 ? void 0 : firstSidebarItem.link), image: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.image) });
213
+ return Object.assign(Object.assign(Object.assign({}, metadata), apiCustomFields), { publishedAt: metadata.publishedAt || metadata.createdAt, title: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.title) || item.label || 'Untitled', description: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.description), link: (_c = (0, utils_1.withoutHash)(link)) !== null && _c !== void 0 ? _c : '#', docsLink: (0, utils_1.withoutHash)(firstSidebarItem === null || firstSidebarItem === void 0 ? void 0 : firstSidebarItem.link), image: (0, utils_1.toStringIfDefined)(metadata === null || metadata === void 0 ? void 0 : metadata.image) });
209
214
  });
210
215
  }
211
216
  function collectFilterParents(filtersWithOptions) {
@@ -35,7 +35,6 @@ const HeaderWrapper_1 = require("../../components/Sidebar/HeaderWrapper");
35
35
  const useMobileMenu_1 = require("../../hooks/useMobileMenu");
36
36
  const MobileSidebarButton_1 = require("../../components/Sidebar/MobileSidebarButton");
37
37
  const MenuContainer_1 = require("../../components/Menu/MenuContainer");
38
- const SidebarSearch_1 = require("../../components/Search/SidebarSearch");
39
38
  const useThemeConfig_1 = require("../../hooks/useThemeConfig");
40
39
  const telemetry_1 = require("../../mocks/telemetry");
41
40
  const MobileSidebarIcon_1 = require("./MobileSidebarIcon");
@@ -56,7 +55,7 @@ function SidebarLayout({ versions, menu, footer, header, growContent, collapsed,
56
55
  onToggleMenu === null || onToggleMenu === void 0 ? void 0 : onToggleMenu(!isOpen);
57
56
  setIsOpen(!isOpen);
58
57
  };
59
- const { search, sidebar } = (0, useThemeConfig_1.useThemeConfig)();
58
+ const { sidebar } = (0, useThemeConfig_1.useThemeConfig)();
60
59
  if (sidebar === null || sidebar === void 0 ? void 0 : sidebar.hide) {
61
60
  return null;
62
61
  }
@@ -64,7 +63,6 @@ function SidebarLayout({ versions, menu, footer, header, growContent, collapsed,
64
63
  react_1.default.createElement(StyledFooterWrapper, { "data-component-name": "Sidebar/FooterWrapper", collapsed: true }, footer),
65
64
  react_1.default.createElement(MobileSidebarButton_1.MobileSidebarButton, { opened: isOpen, onClick: toggleMenu },
66
65
  react_1.default.createElement(MobileSidebarIcon_1.MobileSidebarIcon, null)))) : null) : (react_1.default.createElement(Wrapper, { "data-component-name": "Sidebar/SidebarLayout", className: className },
67
- !(search === null || search === void 0 ? void 0 : search.hide) && (search === null || search === void 0 ? void 0 : search.placement) === 'sidebar' ? react_1.default.createElement(SidebarSearch_1.SidebarSearch, null) : null,
68
66
  react_1.default.createElement(Sidebar_1.Sidebar, { animate: true, opened: isOpen },
69
67
  header ? (react_1.default.createElement(HeaderWrapper_1.HeaderWrapper, { onClick: () => telemetry_1.telemetry.send('back_to_catalog_button_clicked', {}) }, header)) : null,
70
68
  versions ? react_1.default.createElement(react_1.default.Fragment, null, versions) : null,
@@ -2,4 +2,5 @@ import type { ResolvedNavLinkItem } from '../../types/portal';
2
2
  export declare function usePageData(): {
3
3
  prevPage: ResolvedNavLinkItem | null;
4
4
  nextPage: ResolvedNavLinkItem | null;
5
+ props?: any;
5
6
  } | null;
@@ -17,6 +17,7 @@ export type ResolvedNavLinkItem = {
17
17
  httpVerb?: string;
18
18
  separatorLine?: boolean;
19
19
  routeSlug?: string;
20
+ fsPath?: string;
20
21
  active?: boolean;
21
22
  icon?: string;
22
23
  srcSet?: string;
@@ -41,6 +42,7 @@ export type ResolvedNavGroupItem = {
41
42
  menuStyle?: MenuStyle;
42
43
  separatorLine?: boolean;
43
44
  routeSlug?: string;
45
+ fsPath?: string;
44
46
  active?: boolean;
45
47
  icon?: string;
46
48
  srcSet?: string;
@@ -52,6 +54,7 @@ export type ResolvedNavItem = ResolvedNavLinkItem | ResolvedNavGroupItem | {
52
54
  label?: string;
53
55
  labelTranslationKey?: string;
54
56
  routeSlug?: never;
57
+ fsPath?: never;
55
58
  version?: string;
56
59
  isDefault?: boolean;
57
60
  versionFolderId?: string;
@@ -70,6 +73,7 @@ export type ResolvedNavItem = ResolvedNavLinkItem | ResolvedNavGroupItem | {
70
73
  versionFolderId?: string;
71
74
  metadata?: Record<string, unknown>;
72
75
  routeSlug?: never;
76
+ fsPath?: never;
73
77
  label: string;
74
78
  labelTranslationKey?: string;
75
79
  link?: undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.19.0",
3
+ "version": "0.19.2",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -4,6 +4,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
4
4
  import type { Location } from 'react-router-dom';
5
5
 
6
6
  import { telemetry } from '@portal/telemetry';
7
+ import { usePageData } from '@portal/hooks/usePageData';
7
8
  import type { ResolvedNavItem } from '@theme/types/portal';
8
9
  import type {
9
10
  CatalogItem,
@@ -16,6 +17,7 @@ import type { CatalogConfig, CatalogFilterConfig } from '@theme/config';
16
17
  export function useCatalog(items: ResolvedNavItem[], config: CatalogConfig): FilteredCatalog {
17
18
  const location = useLocation();
18
19
  const navigate = useNavigate();
20
+ const pageData = usePageData();
19
21
  const searchParams = useSearchParams(location);
20
22
  const [filtersState, setFiltersState] = React.useState(() =>
21
23
  (config.filters ?? []).map((f) => {
@@ -33,7 +35,12 @@ export function useCatalog(items: ResolvedNavItem[], config: CatalogConfig): Fil
33
35
  () => collectFilterOptions(items, config.filters),
34
36
  [items, config.filters],
35
37
  );
36
- const normalizedItems = React.useMemo(() => normalizeItems(items, config), [items, config]);
38
+
39
+ const customFields = pageData?.props?.customFields;
40
+ const normalizedItems = React.useMemo(
41
+ () => normalizeItems(items, config, customFields || ({} as any)),
42
+ [items, config, customFields],
43
+ );
37
44
 
38
45
  const toggleOption = React.useCallback((filterIdx, option) => {
39
46
  setFiltersState((prev) => {
@@ -198,7 +205,11 @@ function useSearchParams(location: Location) {
198
205
  return React.useMemo(() => new URLSearchParams(location.search), [location.search]);
199
206
  }
200
207
 
201
- function normalizeItems(items: ResolvedNavItem[], config: CatalogConfig): CatalogItem[] {
208
+ function normalizeItems(
209
+ items: ResolvedNavItem[],
210
+ config: CatalogConfig,
211
+ customFields: Record<string, Record<string, unknown>>,
212
+ ): CatalogItem[] {
202
213
  // because of RBAC some versions can be missing so we need to pick up other default version
203
214
  const hasDeafultVersion: Record<string, boolean> = {};
204
215
  for (const item of items) {
@@ -223,10 +234,12 @@ function normalizeItems(items: ResolvedNavItem[], config: CatalogConfig): Catalo
223
234
 
224
235
  return items.map((item) => {
225
236
  const metadata = item.metadata || {};
237
+ const apiCustomFields = customFields[item.fsPath || ''] || {};
226
238
  const link = item.link || findDeepFirst(item.items || [], (i) => 'link' in i && !!i.link)?.link;
227
239
  const firstSidebarItem = (item as any).sidebar?.[0];
228
240
  return {
229
241
  ...metadata,
242
+ ...apiCustomFields,
230
243
  publishedAt: metadata.publishedAt || metadata.createdAt,
231
244
  title: toStringIfDefined(metadata?.title) || item.label || 'Untitled',
232
245
  description: toStringIfDefined(metadata?.description),
@@ -7,7 +7,6 @@ import { HeaderWrapper } from '@theme/components/Sidebar/HeaderWrapper';
7
7
  import { useMobileMenu } from '@theme/hooks/useMobileMenu';
8
8
  import { MobileSidebarButton } from '@theme/components/Sidebar/MobileSidebarButton';
9
9
  import { MenuContainer } from '@theme/components/Menu/MenuContainer';
10
- import { SidebarSearch } from '@theme/components/Search/SidebarSearch';
11
10
  import { useThemeConfig } from '@theme/hooks/useThemeConfig';
12
11
  import { telemetry } from '@portal/telemetry';
13
12
 
@@ -59,7 +58,7 @@ export function SidebarLayout({
59
58
  setIsOpen(!isOpen);
60
59
  };
61
60
 
62
- const { search, sidebar } = useThemeConfig();
61
+ const { sidebar } = useThemeConfig();
63
62
 
64
63
  if (sidebar?.hide) {
65
64
  return null;
@@ -80,7 +79,6 @@ export function SidebarLayout({
80
79
  ) : null
81
80
  ) : (
82
81
  <Wrapper data-component-name="Sidebar/SidebarLayout" className={className}>
83
- {!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
84
82
  <Sidebar animate={true} opened={isOpen}>
85
83
  {header ? (
86
84
  <HeaderWrapper onClick={() => telemetry.send('back_to_catalog_button_clicked', {})}>
@@ -3,6 +3,7 @@ import type { ResolvedNavLinkItem } from '@theme/types/portal';
3
3
  export function usePageData(): {
4
4
  prevPage: ResolvedNavLinkItem | null;
5
5
  nextPage: ResolvedNavLinkItem | null;
6
+ props?: any;
6
7
  } | null {
7
8
  return {
8
9
  prevPage: { label: 'Intro', type: 'link', link: '#prev' },
@@ -20,6 +20,7 @@ export type ResolvedNavLinkItem = {
20
20
  httpVerb?: string;
21
21
  separatorLine?: boolean;
22
22
  routeSlug?: string;
23
+ fsPath?: string
23
24
  active?: boolean;
24
25
  icon?: string;
25
26
  srcSet?: string;
@@ -47,6 +48,7 @@ export type ResolvedNavGroupItem = {
47
48
  menuStyle?: MenuStyle;
48
49
  separatorLine?: boolean;
49
50
  routeSlug?: string;
51
+ fsPath?: string
50
52
  active?: boolean;
51
53
  icon?: string;
52
54
  srcSet?: string;
@@ -62,6 +64,7 @@ export type ResolvedNavItem =
62
64
  label?: string;
63
65
  labelTranslationKey?: string;
64
66
  routeSlug?: never;
67
+ fsPath?: never
65
68
 
66
69
  version?: string;
67
70
  isDefault?: boolean;
@@ -84,6 +87,7 @@ export type ResolvedNavItem =
84
87
  versionFolderId?: string;
85
88
  metadata?: Record<string, unknown>;
86
89
  routeSlug?: never;
90
+ fsPath?: never
87
91
 
88
92
  label: string;
89
93
  labelTranslationKey?: string;
@@ -1,2 +0,0 @@
1
- import { Search } from '../../components/Search/Search';
2
- export declare const SidebarSearch: import("styled-components").StyledComponent<typeof Search, any, {}, never>;
@@ -1,21 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SidebarSearch = void 0;
7
- const styled_components_1 = __importDefault(require("styled-components"));
8
- const Search_1 = require("../../components/Search/Search");
9
- const Popover_1 = require("../../components/Search/Popover");
10
- exports.SidebarSearch = (0, styled_components_1.default)(Search_1.Search).attrs(({ className }) => ({
11
- 'data-component-name': 'Search/SidebarSearch',
12
- className,
13
- })) `
14
- --search-input-text-color: var(--sidebar-item-active-color);
15
-
16
- ${Popover_1.Popover} {
17
- right: unset;
18
- top: 100%;
19
- }
20
- `;
21
- //# sourceMappingURL=SidebarSearch.js.map
@@ -1,16 +0,0 @@
1
- import styled from 'styled-components';
2
-
3
- import { Search } from '@theme/components/Search/Search';
4
- import { Popover } from '@theme/components/Search/Popover';
5
-
6
- export const SidebarSearch = styled(Search).attrs<{ className?: string }>(({ className }) => ({
7
- 'data-component-name': 'Search/SidebarSearch',
8
- className,
9
- }))`
10
- --search-input-text-color: var(--sidebar-item-active-color);
11
-
12
- ${Popover} {
13
- right: unset;
14
- top: 100%;
15
- }
16
- `;