@redocly/theme 0.19.1 → 0.19.3
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.
- package/lib/components/Catalog/Catalog.d.ts +3 -1
- package/lib/components/Catalog/Catalog.js +9 -9
- package/lib/components/Catalog/styledVariables.js +4 -2
- package/lib/components/Catalog/useCatalog.js +10 -5
- package/lib/config.d.ts +2 -2
- package/lib/config.js +1 -1
- package/lib/mocks/hooks/usePageData.d.ts +1 -0
- package/lib/types/portal/src/shared/types/nav.d.ts +4 -0
- package/package.json +1 -1
- package/src/components/Catalog/Catalog.tsx +21 -19
- package/src/components/Catalog/styledVariables.ts +4 -2
- package/src/components/Catalog/useCatalog.ts +16 -3
- package/src/config.ts +1 -1
- package/src/mocks/hooks/usePageData.ts +1 -0
- package/src/types/portal/src/shared/types/nav.ts +4 -0
|
@@ -10,5 +10,7 @@ export declare const FilterControls: import("styled-components").StyledComponent
|
|
|
10
10
|
export declare const CatalogPageContent: import("styled-components").StyledComponent<"main", any, {}, never>;
|
|
11
11
|
export declare const CatalogTitle: import("styled-components").StyledComponent<"h2", any, {}, never>;
|
|
12
12
|
export declare const CatalogDescription: import("styled-components").StyledComponent<"p", any, {}, never>;
|
|
13
|
-
export declare const CatalogPageWrapper: import("styled-components").StyledComponent<"div", any, {
|
|
13
|
+
export declare const CatalogPageWrapper: import("styled-components").StyledComponent<"div", any, {
|
|
14
|
+
withoutFilters?: boolean | undefined;
|
|
15
|
+
}, never>;
|
|
14
16
|
export declare const CatalogPageDescriptionWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
@@ -27,10 +27,10 @@ function Catalog(props) {
|
|
|
27
27
|
const { translate } = (0, index_1.useTranslate)();
|
|
28
28
|
(0, useModalScrollLock_1.default)(isAddingFilter);
|
|
29
29
|
return (react_1.default.createElement(Highlight_1.HighlightContext.Provider, { value: [filterTerm] },
|
|
30
|
-
react_1.default.createElement(exports.CatalogPageWrapper,
|
|
31
|
-
react_1.default.createElement(FilterTagsWrapper, null,
|
|
30
|
+
react_1.default.createElement(exports.CatalogPageWrapper, { withoutFilters: !filters.length },
|
|
31
|
+
!!filters.length && (react_1.default.createElement(FilterTagsWrapper, null,
|
|
32
32
|
react_1.default.createElement(Button_1.Button, { variant: "text", size: "small", icon: react_1.default.createElement(PlusIcon_1.PlusIcon, null), onClick: () => setIsAddingFilter(true) }, translate(translationKeys.addFilter, 'Add filter')),
|
|
33
|
-
react_1.default.createElement(FilterTags_1.FilterTags, { filters: filters })),
|
|
33
|
+
react_1.default.createElement(FilterTags_1.FilterTags, { filters: filters }))),
|
|
34
34
|
react_1.default.createElement(FilterContent_1.FilterContent, { setFilterTerm: setFilterTerm, filters: filters, filterTerm: filterTerm, isMobile: false, filterValuesCasing: catalogConfig.filterValuesCasing }),
|
|
35
35
|
isAddingFilter && (react_1.default.createElement(FilterPopover_1.FilterPopover, { setIsAddingFilter: setIsAddingFilter, filters: filters, filterValuesCasing: catalogConfig.filterValuesCasing })),
|
|
36
36
|
react_1.default.createElement(exports.CatalogPageContent, null,
|
|
@@ -101,16 +101,13 @@ exports.CatalogTitle = (0, styled_components_1.default)(H2_1.H2) `
|
|
|
101
101
|
color: var(--catalog-title-text-color);
|
|
102
102
|
font-weight: var(--catalog-title-font-weight);
|
|
103
103
|
font-size: var(--catalog-title-font-size);
|
|
104
|
-
|
|
105
|
-
&& {
|
|
106
|
-
margin: 0;
|
|
107
|
-
}
|
|
104
|
+
margin: var(--catalog-title-margin);
|
|
108
105
|
`;
|
|
109
106
|
exports.CatalogDescription = styled_components_1.default.p `
|
|
110
107
|
color: var(--catalog-description-text-color);
|
|
111
108
|
font-weight: var(--catalog-description-font-weight);
|
|
112
109
|
font-size: var(--catalog-description-font-size);
|
|
113
|
-
margin: var(--catalog-description-margin
|
|
110
|
+
margin: var(--catalog-description-margin);
|
|
114
111
|
`;
|
|
115
112
|
exports.CatalogPageWrapper = styled_components_1.default.div `
|
|
116
113
|
--sidebar-width: var(--catalog-sidebar-width, 285px);
|
|
@@ -119,7 +116,6 @@ exports.CatalogPageWrapper = styled_components_1.default.div `
|
|
|
119
116
|
flex-direction: column;
|
|
120
117
|
|
|
121
118
|
font-weight: var(--font-weight-regular);
|
|
122
|
-
padding: 0;
|
|
123
119
|
|
|
124
120
|
color: var(--text-secondary);
|
|
125
121
|
font-size: var(--font-size-base);
|
|
@@ -138,8 +134,11 @@ exports.CatalogPageWrapper = styled_components_1.default.div `
|
|
|
138
134
|
font-weight: var(--link-font-weight);
|
|
139
135
|
}
|
|
140
136
|
|
|
137
|
+
padding: ${({ withoutFilters }) => (withoutFilters ? 'var(--spacing-base) 0 0 0' : '0')};
|
|
138
|
+
|
|
141
139
|
${({ theme }) => theme.mediaQueries.medium} {
|
|
142
140
|
flex-direction: row;
|
|
141
|
+
padding: 0;
|
|
143
142
|
}
|
|
144
143
|
`;
|
|
145
144
|
const FilterTagsWrapper = styled_components_1.default.div.attrs({ 'data-cy': 'Catalog/FilterTags' }) `
|
|
@@ -172,6 +171,7 @@ const FilterTagsWrapper = styled_components_1.default.div.attrs({ 'data-cy': 'Ca
|
|
|
172
171
|
}
|
|
173
172
|
`;
|
|
174
173
|
exports.CatalogPageDescriptionWrapper = styled_components_1.default.div `
|
|
174
|
+
margin: var(--catalog-heading-margin);
|
|
175
175
|
display: none;
|
|
176
176
|
|
|
177
177
|
${({ theme }) => theme.mediaQueries.medium} {
|
|
@@ -14,12 +14,15 @@ exports.catalog = (0, styled_components_1.css) `
|
|
|
14
14
|
--mobile-catalog-filter-padding-vertical: var(--spacing-sm);
|
|
15
15
|
--mobile-catalog-filter-padding-horizontal: var(--spacing-base);
|
|
16
16
|
|
|
17
|
+
--catalog-heading-margin: 0 0 var(--spacing-xl) 0;
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* @tokens Catalog page title
|
|
19
21
|
*/
|
|
20
22
|
--catalog-title-text-color: var(--text-primary);
|
|
21
23
|
--catalog-title-font-weight: var(--font-weight-bold);
|
|
22
24
|
--catalog-title-font-size: var(--font-size-heading-3);
|
|
25
|
+
--catalog-title-margin: 0 0 var(--spacing-sm) 0;
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* @tokens Catalog page description
|
|
@@ -27,8 +30,7 @@ exports.catalog = (0, styled_components_1.css) `
|
|
|
27
30
|
--catalog-description-text-color: var(--text-secondary);
|
|
28
31
|
--catalog-description-font-weight: var(--font-weight-regular);
|
|
29
32
|
--catalog-description-font-size: var(--font-size-base);
|
|
30
|
-
--catalog-description-margin
|
|
31
|
-
--catalog-description-margin-bottom: var(--spacing-xl);
|
|
33
|
+
--catalog-description-margin: 0 0 var(--spacing-sm) 0;
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* @tokens Catalog page separator
|
|
@@ -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
|
|
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();
|
|
@@ -115,8 +119,8 @@ function useCatalog(items, config) {
|
|
|
115
119
|
return React.useMemo(() => {
|
|
116
120
|
var _a;
|
|
117
121
|
const filters = filtersWithOptions.map((filter, idx) => {
|
|
118
|
-
var _a, _b, _c;
|
|
119
|
-
return (Object.assign(Object.assign({}, filter), { toggleOption: (value) => toggleOption(idx, value), selectOption: (value) => selectOption(idx, value), selectedOptions: (_a = filtersState[idx]) !== null && _a !== void 0 ? _a : new Set(), isFilterUsed: ((_c = (_b = filtersState[idx]) === null || _b === void 0 ? void 0 : _b.size) !== null && _c !== void 0 ? _c : 0) > 0 || !!filtersState[idx].from }));
|
|
122
|
+
var _a, _b, _c, _d;
|
|
123
|
+
return (Object.assign(Object.assign({}, filter), { toggleOption: (value) => toggleOption(idx, value), selectOption: (value) => selectOption(idx, value), selectedOptions: (_a = filtersState[idx]) !== null && _a !== void 0 ? _a : new Set(), isFilterUsed: ((_c = (_b = filtersState[idx]) === null || _b === void 0 ? void 0 : _b.size) !== null && _c !== void 0 ? _c : 0) > 0 || !!((_d = filtersState[idx]) === null || _d === void 0 ? void 0 : _d.from) }));
|
|
120
124
|
});
|
|
121
125
|
const filteredItems = filterItems(normalizedItems, filters, filterTerm);
|
|
122
126
|
// add more information to filters state which is known only after filtering items
|
|
@@ -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) {
|
package/lib/config.d.ts
CHANGED
|
@@ -356,7 +356,7 @@ declare const scorecardConfigSchema: {
|
|
|
356
356
|
declare const catalogSchema: {
|
|
357
357
|
readonly type: "object";
|
|
358
358
|
readonly additionalProperties: true;
|
|
359
|
-
readonly required: readonly ["slug", "
|
|
359
|
+
readonly required: readonly ["slug", "items"];
|
|
360
360
|
readonly properties: {
|
|
361
361
|
readonly slug: {
|
|
362
362
|
readonly type: "string";
|
|
@@ -1590,7 +1590,7 @@ export declare const themeConfigSchema: {
|
|
|
1590
1590
|
readonly '.*': {
|
|
1591
1591
|
readonly type: "object";
|
|
1592
1592
|
readonly additionalProperties: true;
|
|
1593
|
-
readonly required: readonly ["slug", "
|
|
1593
|
+
readonly required: readonly ["slug", "items"];
|
|
1594
1594
|
readonly properties: {
|
|
1595
1595
|
readonly slug: {
|
|
1596
1596
|
readonly type: "string";
|
package/lib/config.js
CHANGED
|
@@ -314,7 +314,7 @@ const scorecardConfigSchema = {
|
|
|
314
314
|
const catalogSchema = {
|
|
315
315
|
type: 'object',
|
|
316
316
|
additionalProperties: true,
|
|
317
|
-
required: ['slug', '
|
|
317
|
+
required: ['slug', 'items'],
|
|
318
318
|
properties: {
|
|
319
319
|
slug: { type: 'string' },
|
|
320
320
|
filters: { type: 'array', items: catalogFilterSchema },
|
|
@@ -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
|
@@ -35,18 +35,20 @@ export default function Catalog(props: {
|
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<HighlightContext.Provider value={[filterTerm]}>
|
|
38
|
-
<CatalogPageWrapper>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
<CatalogPageWrapper withoutFilters={!filters.length}>
|
|
39
|
+
{!!filters.length && (
|
|
40
|
+
<FilterTagsWrapper>
|
|
41
|
+
<Button
|
|
42
|
+
variant="text"
|
|
43
|
+
size="small"
|
|
44
|
+
icon={<PlusIcon />}
|
|
45
|
+
onClick={() => setIsAddingFilter(true)}
|
|
46
|
+
>
|
|
47
|
+
{translate(translationKeys.addFilter, 'Add filter')}
|
|
48
|
+
</Button>
|
|
49
|
+
<FilterTags filters={filters} />
|
|
50
|
+
</FilterTagsWrapper>
|
|
51
|
+
)}
|
|
50
52
|
<FilterContent
|
|
51
53
|
setFilterTerm={setFilterTerm}
|
|
52
54
|
filters={filters}
|
|
@@ -152,27 +154,23 @@ export const CatalogTitle = styled(H2)`
|
|
|
152
154
|
color: var(--catalog-title-text-color);
|
|
153
155
|
font-weight: var(--catalog-title-font-weight);
|
|
154
156
|
font-size: var(--catalog-title-font-size);
|
|
155
|
-
|
|
156
|
-
&& {
|
|
157
|
-
margin: 0;
|
|
158
|
-
}
|
|
157
|
+
margin: var(--catalog-title-margin);
|
|
159
158
|
`;
|
|
160
159
|
|
|
161
160
|
export const CatalogDescription = styled.p`
|
|
162
161
|
color: var(--catalog-description-text-color);
|
|
163
162
|
font-weight: var(--catalog-description-font-weight);
|
|
164
163
|
font-size: var(--catalog-description-font-size);
|
|
165
|
-
margin: var(--catalog-description-margin
|
|
164
|
+
margin: var(--catalog-description-margin);
|
|
166
165
|
`;
|
|
167
166
|
|
|
168
|
-
export const CatalogPageWrapper = styled.div
|
|
167
|
+
export const CatalogPageWrapper = styled.div<{ withoutFilters?: boolean }>`
|
|
169
168
|
--sidebar-width: var(--catalog-sidebar-width, 285px);
|
|
170
169
|
|
|
171
170
|
display: flex;
|
|
172
171
|
flex-direction: column;
|
|
173
172
|
|
|
174
173
|
font-weight: var(--font-weight-regular);
|
|
175
|
-
padding: 0;
|
|
176
174
|
|
|
177
175
|
color: var(--text-secondary);
|
|
178
176
|
font-size: var(--font-size-base);
|
|
@@ -191,8 +189,11 @@ export const CatalogPageWrapper = styled.div`
|
|
|
191
189
|
font-weight: var(--link-font-weight);
|
|
192
190
|
}
|
|
193
191
|
|
|
192
|
+
padding: ${({ withoutFilters }) => (withoutFilters ? 'var(--spacing-base) 0 0 0' : '0')};
|
|
193
|
+
|
|
194
194
|
${({ theme }) => theme.mediaQueries.medium} {
|
|
195
195
|
flex-direction: row;
|
|
196
|
+
padding: 0;
|
|
196
197
|
}
|
|
197
198
|
`;
|
|
198
199
|
|
|
@@ -227,6 +228,7 @@ const FilterTagsWrapper = styled.div.attrs({ 'data-cy': 'Catalog/FilterTags' })`
|
|
|
227
228
|
`;
|
|
228
229
|
|
|
229
230
|
export const CatalogPageDescriptionWrapper = styled.div`
|
|
231
|
+
margin: var(--catalog-heading-margin);
|
|
230
232
|
display: none;
|
|
231
233
|
|
|
232
234
|
${({ theme }) => theme.mediaQueries.medium} {
|
|
@@ -12,12 +12,15 @@ export const catalog = css`
|
|
|
12
12
|
--mobile-catalog-filter-padding-vertical: var(--spacing-sm);
|
|
13
13
|
--mobile-catalog-filter-padding-horizontal: var(--spacing-base);
|
|
14
14
|
|
|
15
|
+
--catalog-heading-margin: 0 0 var(--spacing-xl) 0;
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* @tokens Catalog page title
|
|
17
19
|
*/
|
|
18
20
|
--catalog-title-text-color: var(--text-primary);
|
|
19
21
|
--catalog-title-font-weight: var(--font-weight-bold);
|
|
20
22
|
--catalog-title-font-size: var(--font-size-heading-3);
|
|
23
|
+
--catalog-title-margin: 0 0 var(--spacing-sm) 0;
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* @tokens Catalog page description
|
|
@@ -25,8 +28,7 @@ export const catalog = css`
|
|
|
25
28
|
--catalog-description-text-color: var(--text-secondary);
|
|
26
29
|
--catalog-description-font-weight: var(--font-weight-regular);
|
|
27
30
|
--catalog-description-font-size: var(--font-size-base);
|
|
28
|
-
--catalog-description-margin
|
|
29
|
-
--catalog-description-margin-bottom: var(--spacing-xl);
|
|
31
|
+
--catalog-description-margin: 0 0 var(--spacing-sm) 0;
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* @tokens Catalog page separator
|
|
@@ -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
|
-
|
|
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) => {
|
|
@@ -114,7 +121,7 @@ export function useCatalog(items: ResolvedNavItem[], config: CatalogConfig): Fil
|
|
|
114
121
|
selectOption: (value: string) => selectOption(idx, value),
|
|
115
122
|
selectedOptions: filtersState[idx] ?? new Set<string>(),
|
|
116
123
|
isFilterUsed:
|
|
117
|
-
((filtersState[idx] as any)?.size ?? 0) > 0 || !!(filtersState[idx] as any)
|
|
124
|
+
((filtersState[idx] as any)?.size ?? 0) > 0 || !!(filtersState[idx] as any)?.from,
|
|
118
125
|
}));
|
|
119
126
|
|
|
120
127
|
const filteredItems = filterItems(normalizedItems, filters, filterTerm);
|
|
@@ -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(
|
|
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),
|
package/src/config.ts
CHANGED
|
@@ -354,7 +354,7 @@ const scorecardConfigSchema = {
|
|
|
354
354
|
const catalogSchema = {
|
|
355
355
|
type: 'object',
|
|
356
356
|
additionalProperties: true,
|
|
357
|
-
required: ['slug', '
|
|
357
|
+
required: ['slug', 'items'],
|
|
358
358
|
properties: {
|
|
359
359
|
slug: { type: 'string' },
|
|
360
360
|
filters: { type: 'array', items: catalogFilterSchema },
|
|
@@ -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;
|