@redocly/theme 0.6.0 → 0.6.2-beta.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/README.md +1 -1
- package/lib/ColorModeSwitcher/ColorModeSwitcher.js +1 -1
- package/lib/Footer/Footer.d.ts +6 -3
- package/lib/Footer/Footer.js +3 -3
- package/lib/PageNavigation/NextButton.d.ts +2 -0
- package/lib/PageNavigation/{NextPageLink.js → NextButton.js} +7 -7
- package/lib/PageNavigation/PageNavigation.js +5 -5
- package/lib/PageNavigation/PreviousButton.d.ts +2 -0
- package/lib/PageNavigation/{PreviousPageLink.js → PreviousButton.js} +7 -7
- package/lib/PageNavigation/index.d.ts +2 -2
- package/lib/PageNavigation/index.js +2 -2
- package/lib/Sidebar/ArrowBack.js +2 -2
- package/lib/Sidebar/SidebarLayout.d.ts +5 -1
- package/lib/Sidebar/SidebarLayout.js +26 -1
- package/lib/Sidebar/types/NavItem.d.ts +1 -1
- package/lib/TableOfContent/TableOfContent.js +14 -19
- package/lib/TableOfContent/utils.d.ts +1 -1
- package/lib/TableOfContent/utils.js +2 -2
- package/lib/config.d.ts +342 -72
- package/lib/config.js +61 -17
- package/lib/globalStyle.js +1 -1
- package/lib/mocks/hooks/index.js +4 -5
- package/package.json +3 -3
- package/src/ColorModeSwitcher/ColorModeSwitcher.tsx +1 -1
- package/src/Footer/Footer.tsx +8 -5
- package/src/PageNavigation/{NextPageLink.tsx → NextButton.tsx} +4 -4
- package/src/PageNavigation/PageNavigation.tsx +5 -5
- package/src/PageNavigation/{PreviousPageLink.tsx → PreviousButton.tsx} +4 -4
- package/src/PageNavigation/index.ts +2 -2
- package/src/Sidebar/ArrowBack.tsx +2 -2
- package/src/Sidebar/SidebarLayout.tsx +38 -1
- package/src/Sidebar/types/NavItem.ts +1 -1
- package/src/TableOfContent/TableOfContent.tsx +24 -32
- package/src/TableOfContent/utils.ts +2 -2
- package/src/config.ts +73 -24
- package/src/globalStyle.ts +1 -1
- package/src/mocks/hooks/index.ts +4 -5
- package/src/settings.yaml +2 -2
- package/src/types/portal/src/shared/constants.d.ts +0 -1
- package/src/types/portal/src/shared/types/nav.d.ts +1 -2
- package/lib/PageNavigation/NextPageLink.d.ts +0 -2
- package/lib/PageNavigation/PreviousPageLink.d.ts +0 -2
package/lib/config.js
CHANGED
|
@@ -13,22 +13,67 @@ const LogoConfig = zod_1.z
|
|
|
13
13
|
const HideConfig = zod_1.z.object({
|
|
14
14
|
hide: zod_1.z.boolean().optional(),
|
|
15
15
|
});
|
|
16
|
+
const ScriptConfig = zod_1.z
|
|
17
|
+
.object({
|
|
18
|
+
src: zod_1.z.string(),
|
|
19
|
+
async: zod_1.z.boolean().optional(),
|
|
20
|
+
crossorigin: zod_1.z.string().optional(),
|
|
21
|
+
defer: zod_1.z.boolean().optional(),
|
|
22
|
+
fetchpriority: zod_1.z.string().optional(),
|
|
23
|
+
integrity: zod_1.z.string().optional(),
|
|
24
|
+
module: zod_1.z.boolean().optional(),
|
|
25
|
+
nomodule: zod_1.z.boolean().optional(),
|
|
26
|
+
nonce: zod_1.z.string().optional(),
|
|
27
|
+
referrerpolicy: zod_1.z.string().optional(),
|
|
28
|
+
type: zod_1.z.string().optional(),
|
|
29
|
+
})
|
|
30
|
+
.passthrough();
|
|
31
|
+
const LinksConfig = zod_1.z
|
|
32
|
+
.object({
|
|
33
|
+
href: zod_1.z.string(),
|
|
34
|
+
as: zod_1.z.string().optional(),
|
|
35
|
+
crossorigin: zod_1.z.string().optional(),
|
|
36
|
+
fetchpriority: zod_1.z.string().optional(),
|
|
37
|
+
hreflang: zod_1.z.string().optional(),
|
|
38
|
+
imagesizes: zod_1.z.string().optional(),
|
|
39
|
+
imagesrcset: zod_1.z.string().optional(),
|
|
40
|
+
integrity: zod_1.z.string().optional(),
|
|
41
|
+
media: zod_1.z.string().optional(),
|
|
42
|
+
prefetch: zod_1.z.string().optional(),
|
|
43
|
+
referrerpolicy: zod_1.z.string().optional(),
|
|
44
|
+
rel: zod_1.z.string().optional(),
|
|
45
|
+
sizes: zod_1.z.string().optional(),
|
|
46
|
+
title: zod_1.z.string().optional(),
|
|
47
|
+
type: zod_1.z.string().optional(),
|
|
48
|
+
})
|
|
49
|
+
.passthrough();
|
|
16
50
|
exports.ThemeConfig = zod_1.z
|
|
17
51
|
.object({
|
|
18
52
|
imports: zod_1.z.array(zod_1.z.string()).default([]).optional(),
|
|
19
53
|
logo: LogoConfig.optional(),
|
|
20
|
-
navbar:
|
|
21
|
-
|
|
54
|
+
navbar: zod_1.z
|
|
55
|
+
.object({
|
|
56
|
+
items: zod_1.z.array(zod_1.z.any()).optional(), // todo: think about validation here
|
|
57
|
+
})
|
|
58
|
+
.extend(HideConfig.shape)
|
|
59
|
+
.strict()
|
|
60
|
+
.optional(),
|
|
61
|
+
footer: zod_1.z
|
|
62
|
+
.object({
|
|
63
|
+
items: zod_1.z.array(zod_1.z.any()).optional(),
|
|
64
|
+
copyrightText: zod_1.z.string().optional(),
|
|
65
|
+
})
|
|
66
|
+
.extend(HideConfig.shape)
|
|
67
|
+
.strict()
|
|
68
|
+
.optional(),
|
|
22
69
|
sidebar: HideConfig.strict().optional(),
|
|
23
|
-
navbarItems: zod_1.z.any().optional(),
|
|
24
|
-
footerItems: zod_1.z.any().optional(),
|
|
25
70
|
scripts: zod_1.z
|
|
26
|
-
.
|
|
27
|
-
.optional(),
|
|
28
|
-
|
|
29
|
-
|
|
71
|
+
.object({
|
|
72
|
+
head: zod_1.z.array(ScriptConfig).optional(),
|
|
73
|
+
body: zod_1.z.array(ScriptConfig).optional(),
|
|
74
|
+
})
|
|
30
75
|
.optional(),
|
|
31
|
-
|
|
76
|
+
links: zod_1.z.array(LinksConfig).optional(),
|
|
32
77
|
search: zod_1.z
|
|
33
78
|
.object({
|
|
34
79
|
placement: zod_1.z.string().default('navbar').optional(),
|
|
@@ -39,8 +84,7 @@ exports.ThemeConfig = zod_1.z
|
|
|
39
84
|
.optional(),
|
|
40
85
|
colorMode: zod_1.z
|
|
41
86
|
.object({
|
|
42
|
-
|
|
43
|
-
default: zod_1.z.string().optional(),
|
|
87
|
+
ignoreDetection: zod_1.z.boolean().optional(),
|
|
44
88
|
modes: zod_1.z.array(zod_1.z.string()).default(['light', 'dark']).optional(),
|
|
45
89
|
})
|
|
46
90
|
.extend(HideConfig.shape)
|
|
@@ -49,13 +93,13 @@ exports.ThemeConfig = zod_1.z
|
|
|
49
93
|
.default({}),
|
|
50
94
|
navigation: zod_1.z
|
|
51
95
|
.object({
|
|
52
|
-
|
|
53
|
-
.object({
|
|
96
|
+
nextButton: zod_1.z
|
|
97
|
+
.object({ text: zod_1.z.string().default('Next to {label}') })
|
|
54
98
|
.extend(HideConfig.shape)
|
|
55
99
|
.optional()
|
|
56
100
|
.default({}),
|
|
57
|
-
|
|
58
|
-
.object({
|
|
101
|
+
previousButton: zod_1.z
|
|
102
|
+
.object({ text: zod_1.z.string().default('Back to {label}') })
|
|
59
103
|
.extend(HideConfig.shape)
|
|
60
104
|
.optional()
|
|
61
105
|
.default({}),
|
|
@@ -65,7 +109,7 @@ exports.ThemeConfig = zod_1.z
|
|
|
65
109
|
.default({}),
|
|
66
110
|
markdown: zod_1.z
|
|
67
111
|
.object({
|
|
68
|
-
|
|
112
|
+
frontMatterKeysToResolve: zod_1.z.array(zod_1.z.string()).default(['image', 'links']).optional(),
|
|
69
113
|
lastUpdatedBlock: zod_1.z
|
|
70
114
|
.object({
|
|
71
115
|
format: zod_1.z.enum(['timeago', 'iso', 'long', 'short']).default('timeago').optional(),
|
|
@@ -77,7 +121,7 @@ exports.ThemeConfig = zod_1.z
|
|
|
77
121
|
toc: zod_1.z
|
|
78
122
|
.object({
|
|
79
123
|
header: zod_1.z.string().default('On this page').optional(),
|
|
80
|
-
|
|
124
|
+
depth: zod_1.z.number().default(3).optional(),
|
|
81
125
|
})
|
|
82
126
|
.extend(HideConfig.shape)
|
|
83
127
|
.optional()
|
package/lib/globalStyle.js
CHANGED
|
@@ -512,7 +512,7 @@ const sidebar = (0, styled_components_1.css) `
|
|
|
512
512
|
--sidebar-back-button-font-family: var(--sidebar-item-font-family);
|
|
513
513
|
--sidebar-back-button-font-size: var(--sidebar-item-font-size);
|
|
514
514
|
--sidebar-back-button-transform: inherit;
|
|
515
|
-
--sidebar-back-button-text-color: var(--
|
|
515
|
+
--sidebar-back-button-text-color: var(--link-text-color);
|
|
516
516
|
--sidebar-back-button-background-color: transparent;
|
|
517
517
|
--sidebar-back-button-hover-text-color: var(--sidebar-item-active-color);
|
|
518
518
|
--sidebar-back-button-hover-background-color: transparent;
|
package/lib/mocks/hooks/index.js
CHANGED
|
@@ -5,7 +5,7 @@ function useThemeConfig() {
|
|
|
5
5
|
return {
|
|
6
6
|
search: { hide: false, placement: 'navbar' },
|
|
7
7
|
markdown: {
|
|
8
|
-
toc: {
|
|
8
|
+
toc: { depth: 3, header: 'Table of contents', hide: false },
|
|
9
9
|
lastUpdatedBlock: { hide: false, format: 'timeago', locale: 'en-US' },
|
|
10
10
|
copyCodeSnippet: {
|
|
11
11
|
hide: false,
|
|
@@ -18,15 +18,14 @@ function useThemeConfig() {
|
|
|
18
18
|
baseUrl: '',
|
|
19
19
|
text: 'Edit this page',
|
|
20
20
|
},
|
|
21
|
-
|
|
21
|
+
frontMatterKeysToResolve: ['image', 'links'],
|
|
22
22
|
},
|
|
23
23
|
navigation: {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
nextButton: { text: 'next page theme config label' },
|
|
25
|
+
previousButton: { text: 'prev page theme config label' },
|
|
26
26
|
},
|
|
27
27
|
colorMode: {
|
|
28
28
|
modes: ['light', 'dark'],
|
|
29
|
-
default: 'light',
|
|
30
29
|
},
|
|
31
30
|
};
|
|
32
31
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2-beta.3",
|
|
4
4
|
"description": "Shared UI components",
|
|
5
5
|
"author": "team@redocly.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@storybook/manager-webpack5": "^6.5.9",
|
|
57
57
|
"@storybook/node-logger": "^6.5.9",
|
|
58
58
|
"@storybook/react": "^6.5.9",
|
|
59
|
-
"@storybook/testing-library": "^0.0.
|
|
59
|
+
"@storybook/testing-library": "^0.0.13",
|
|
60
60
|
"@storybook/theming": "^6.5.9",
|
|
61
61
|
"@testing-library/jest-dom": "^5.16.5",
|
|
62
62
|
"@testing-library/react": "^12.1.4",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@types/react": "^17.0.43",
|
|
71
71
|
"@types/react-dom": "^17.0.14",
|
|
72
72
|
"@types/react-router-dom": "^5.3.1",
|
|
73
|
-
"@types/styled-components": "^5.1.
|
|
73
|
+
"@types/styled-components": "^5.1.26",
|
|
74
74
|
"@types/styled-system": "^5.1.13",
|
|
75
75
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
|
76
76
|
"@typescript-eslint/parser": "^5.23.0",
|
|
@@ -9,7 +9,7 @@ export function ColorModeSwitcher(): JSX.Element | null {
|
|
|
9
9
|
const colorMode = themeSettings.colorMode;
|
|
10
10
|
const [activeColorMode, setActiveColorMode] = useState('');
|
|
11
11
|
const modes = colorMode?.modes || ['light', 'dark'];
|
|
12
|
-
const defaultColor =
|
|
12
|
+
const defaultColor = modes[0] || 'light';
|
|
13
13
|
|
|
14
14
|
useMount(() => {
|
|
15
15
|
setActiveColorMode(document.documentElement.className || defaultColor);
|
package/src/Footer/Footer.tsx
CHANGED
|
@@ -5,22 +5,25 @@ import { FooterColumns } from '@theme/Footer/FooterColumns';
|
|
|
5
5
|
import { FooterCopyright } from '@theme/Footer/FooterCopyright';
|
|
6
6
|
import { isEmptyArray } from '@theme/utils';
|
|
7
7
|
import { useThemeConfig } from '@theme/hooks';
|
|
8
|
-
import type {
|
|
8
|
+
import type { NavGroup, ResolvedNavItem } from '@theme/types/portal';
|
|
9
9
|
|
|
10
10
|
interface FooterProps {
|
|
11
|
-
data:
|
|
11
|
+
data: {
|
|
12
|
+
items?: NavGroup;
|
|
13
|
+
copyrightText?: string;
|
|
14
|
+
};
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
export function Footer({ data: {
|
|
17
|
+
export function Footer({ data: { items, copyrightText } }: FooterProps): JSX.Element | null {
|
|
15
18
|
const { footer } = useThemeConfig();
|
|
16
19
|
|
|
17
|
-
if (isEmptyArray(
|
|
20
|
+
if (isEmptyArray(items) || !copyrightText || footer?.hide) {
|
|
18
21
|
return null;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
return (
|
|
22
25
|
<FooterContainer data-component-name="Footer/Footer">
|
|
23
|
-
<FooterColumns columns={
|
|
26
|
+
<FooterColumns columns={items as ResolvedNavItem[]} />
|
|
24
27
|
<FooterCopyright copyrightText={copyrightText} />
|
|
25
28
|
</FooterContainer>
|
|
26
29
|
);
|
|
@@ -10,15 +10,15 @@ interface NextPageType {
|
|
|
10
10
|
nextPage?: ResolvedNavItemWithLink | null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function
|
|
13
|
+
export function NextButton(): JSX.Element {
|
|
14
14
|
const { nextPage }: NextPageType = useSidebarSiblingsData() || {};
|
|
15
15
|
const { navigation } = useThemeConfig();
|
|
16
16
|
|
|
17
|
-
if (!nextPage || navigation?.
|
|
17
|
+
if (!nextPage || navigation?.nextButton?.hide) {
|
|
18
18
|
return <div> </div>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const text = (navigation?.nextButton?.text || 'Next to {label}').replace(
|
|
22
22
|
'{label}',
|
|
23
23
|
nextPage.label || nextPage.routeSlug || '',
|
|
24
24
|
);
|
|
@@ -30,7 +30,7 @@ export function NextPageLink(): JSX.Element {
|
|
|
30
30
|
to={nextPage.link}
|
|
31
31
|
data-component-name="PageNavigation/NextPageLink"
|
|
32
32
|
>
|
|
33
|
-
{
|
|
33
|
+
{text}
|
|
34
34
|
</StyledButton>
|
|
35
35
|
);
|
|
36
36
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { PreviousButton } from '@theme/PageNavigation/PreviousButton';
|
|
5
|
+
import { NextButton } from '@theme/PageNavigation/NextButton';
|
|
6
6
|
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
7
7
|
|
|
8
8
|
export function PageNavigation(): JSX.Element | null {
|
|
9
9
|
const { navigation } = useThemeConfig();
|
|
10
10
|
|
|
11
|
-
if (navigation?.
|
|
11
|
+
if (navigation?.previousButton?.hide && navigation?.nextButton?.hide) {
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<PageNavigationWrapper data-component-name="PageNavigation/PageNavigation">
|
|
17
|
-
<
|
|
18
|
-
<
|
|
17
|
+
<PreviousButton />
|
|
18
|
+
<NextButton />
|
|
19
19
|
</PageNavigationWrapper>
|
|
20
20
|
);
|
|
21
21
|
}
|
|
@@ -10,15 +10,15 @@ interface PreviousPageType {
|
|
|
10
10
|
prevPage?: ResolvedNavItemWithLink | null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function
|
|
13
|
+
export function PreviousButton(): JSX.Element {
|
|
14
14
|
const { prevPage }: PreviousPageType = useSidebarSiblingsData() || {};
|
|
15
15
|
const { navigation } = useThemeConfig();
|
|
16
16
|
|
|
17
|
-
if (!prevPage || navigation?.
|
|
17
|
+
if (!prevPage || navigation?.previousButton?.hide) {
|
|
18
18
|
return <div> </div>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const text = (navigation?.previousButton?.text || 'Back to {label}').replace(
|
|
22
22
|
'{label}',
|
|
23
23
|
prevPage.label || prevPage.routeSlug || '',
|
|
24
24
|
);
|
|
@@ -30,7 +30,7 @@ export function PreviousPageLink(): JSX.Element {
|
|
|
30
30
|
to={prevPage.link}
|
|
31
31
|
data-component-name="PageNavigation/PreviousPageLink"
|
|
32
32
|
>
|
|
33
|
-
{
|
|
33
|
+
{text}
|
|
34
34
|
</StyledButton>
|
|
35
35
|
);
|
|
36
36
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from '@theme/PageNavigation/
|
|
1
|
+
export * from '@theme/PageNavigation/NextButton';
|
|
2
2
|
export * from '@theme/PageNavigation/PageNavigation';
|
|
3
|
-
export * from '@theme/PageNavigation/
|
|
3
|
+
export * from '@theme/PageNavigation/PreviousButton';
|
|
@@ -7,7 +7,7 @@ const Arrow = ({ className }: { className?: string }): JSX.Element => (
|
|
|
7
7
|
fill="none"
|
|
8
8
|
xmlns="http://www.w3.org/2000/svg"
|
|
9
9
|
viewBox="0 0 12 10"
|
|
10
|
-
width="
|
|
10
|
+
width="10px"
|
|
11
11
|
height="10px"
|
|
12
12
|
className={className}
|
|
13
13
|
>
|
|
@@ -19,7 +19,7 @@ const Arrow = ({ className }: { className?: string }): JSX.Element => (
|
|
|
19
19
|
|
|
20
20
|
export const ArrowBack = styled(Arrow)`
|
|
21
21
|
fill: var(--sidebar-back-button-icon-color);
|
|
22
|
-
margin-right: calc(var(--sidebar-spacing-unit)
|
|
22
|
+
margin-right: calc(var(--sidebar-spacing-unit));
|
|
23
23
|
|
|
24
24
|
background-image: var(--sidebar-back-button-icon);
|
|
25
25
|
background-repeat: no-repeat;
|
|
@@ -7,13 +7,23 @@ import { MobileSidebarButton } from '@theme/Sidebar/MobileSidebarButton';
|
|
|
7
7
|
import { MenuContainer } from '@theme/Sidebar/MenuContainer';
|
|
8
8
|
import { SidebarSearch } from '@theme/Search/SidebarSearch';
|
|
9
9
|
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
10
|
+
import { ArrowBack } from '@theme/Sidebar/ArrowBack';
|
|
11
|
+
import { Link } from '@portal/Link';
|
|
10
12
|
|
|
11
13
|
interface SidebarLayoutProps {
|
|
12
14
|
versions: React.ReactNode;
|
|
13
15
|
menu: React.ReactNode;
|
|
16
|
+
backLink?: {
|
|
17
|
+
label: string;
|
|
18
|
+
slug: string;
|
|
19
|
+
};
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
export function SidebarLayout({
|
|
22
|
+
export function SidebarLayout({
|
|
23
|
+
versions,
|
|
24
|
+
menu,
|
|
25
|
+
backLink,
|
|
26
|
+
}: SidebarLayoutProps): JSX.Element | null {
|
|
17
27
|
const [isOpen, setIsOpen] = useMobileMenu();
|
|
18
28
|
const toggleMenu = () => setIsOpen(!isOpen);
|
|
19
29
|
const { search, sidebar } = useThemeConfig();
|
|
@@ -28,6 +38,15 @@ export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Eleme
|
|
|
28
38
|
|
|
29
39
|
{!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
|
|
30
40
|
<Sidebar animate={true} opened={isOpen}>
|
|
41
|
+
{(backLink && (
|
|
42
|
+
<BackLinkWrapper>
|
|
43
|
+
<Link to={backLink.slug}>
|
|
44
|
+
<ArrowBack />
|
|
45
|
+
Back to {backLink.label}
|
|
46
|
+
</Link>
|
|
47
|
+
</BackLinkWrapper>
|
|
48
|
+
)) ||
|
|
49
|
+
null}
|
|
31
50
|
{versions}
|
|
32
51
|
<MenuContainer>{menu}</MenuContainer>
|
|
33
52
|
</Sidebar>
|
|
@@ -35,4 +54,22 @@ export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Eleme
|
|
|
35
54
|
);
|
|
36
55
|
}
|
|
37
56
|
|
|
57
|
+
const BackLinkWrapper = styled.div`
|
|
58
|
+
padding: var(--sidebar-offset-top) var(--sidebar-item-padding-horizontal)
|
|
59
|
+
var(--sidebar-item-padding-horizontal)
|
|
60
|
+
calc(var(--sidebar-offset-left) + var(--sidebar-item-padding-horizontal));
|
|
61
|
+
|
|
62
|
+
a {
|
|
63
|
+
color: var(--sidebar-back-button-text-color);
|
|
64
|
+
font-size: var(--sidebar-back-button-font-size);
|
|
65
|
+
font-family: var(--sidebar-back-button-font-family);
|
|
66
|
+
text-decoration: none;
|
|
67
|
+
&:hover {
|
|
68
|
+
color: var(--sidebar-back-button-hover-text-color);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
border-bottom: 1px solid var(--sidebar-border-color);
|
|
73
|
+
`;
|
|
74
|
+
|
|
38
75
|
const Wrapper = styled.div``;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { useFullHeight } from '@theme/hooks/useFullHeight';
|
|
@@ -16,11 +16,11 @@ interface TableOfContentProps {
|
|
|
16
16
|
export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
|
|
17
17
|
const { headings, contentWrapper } = props;
|
|
18
18
|
|
|
19
|
-
const sidebar = useRef<HTMLDivElement | null>(null);
|
|
19
|
+
const sidebar = React.useRef<HTMLDivElement | null>(null);
|
|
20
20
|
useFullHeight(sidebar);
|
|
21
21
|
const { markdown: { toc = {} } = {} } = useThemeConfig();
|
|
22
22
|
|
|
23
|
-
const displayedHeadings = getDisplayedHeadings(headings, toc.
|
|
23
|
+
const displayedHeadings = getDisplayedHeadings(headings, toc.depth || 3);
|
|
24
24
|
const leastDepth = getLeastDepth(displayedHeadings);
|
|
25
25
|
|
|
26
26
|
const activeHeadingId = useActiveHeading(
|
|
@@ -31,38 +31,30 @@ export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
|
|
|
31
31
|
if (toc?.hide) {
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
|
-
if (headings && headings.length === 1 && (!headings[0] || headings[0].depth === 1)) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (!headings?.length) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
34
|
|
|
41
35
|
return (
|
|
42
36
|
<>
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</TableOfContentMenu>
|
|
65
|
-
)}
|
|
37
|
+
<TableOfContentMenu data-component-name="TableOfContent/TableOfContent">
|
|
38
|
+
<TableOfContentItems ref={sidebar}>
|
|
39
|
+
{displayedHeadings.length ? <TocHeader>{toc.header || 'On this page'}</TocHeader> : null}
|
|
40
|
+
{displayedHeadings.map((heading: MdHeading | null, idx: number) => {
|
|
41
|
+
if (!heading) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const href = '#' + heading.id;
|
|
45
|
+
return (
|
|
46
|
+
<MenuItem
|
|
47
|
+
key={href + idx}
|
|
48
|
+
href={href}
|
|
49
|
+
depth={heading.depth - leastDepth + 1 || 0}
|
|
50
|
+
className={activeHeadingId === heading.id ? 'active' : ''}
|
|
51
|
+
dangerouslySetInnerHTML={{ __html: heading.value || '' }}
|
|
52
|
+
data-cy={`toc-${heading.value}`}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
</TableOfContentItems>
|
|
57
|
+
</TableOfContentMenu>
|
|
66
58
|
</>
|
|
67
59
|
);
|
|
68
60
|
}
|
|
@@ -2,7 +2,7 @@ import type { MdHeading } from '@theme/types/portal';
|
|
|
2
2
|
|
|
3
3
|
export function getDisplayedHeadings(
|
|
4
4
|
headings: Array<MdHeading | null> | null | undefined,
|
|
5
|
-
|
|
5
|
+
tocDepth: number,
|
|
6
6
|
): Array<MdHeading | null> {
|
|
7
7
|
if (!headings) {
|
|
8
8
|
return [];
|
|
@@ -14,7 +14,7 @@ export function getDisplayedHeadings(
|
|
|
14
14
|
if (idx === 0 && heading.depth === 1) {
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
|
-
return !(heading.depth && heading.depth >
|
|
17
|
+
return !(heading.depth && heading.depth > tocDepth);
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
|
package/src/config.ts
CHANGED
|
@@ -13,24 +13,73 @@ const HideConfig = z.object({
|
|
|
13
13
|
hide: z.boolean().optional(),
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
const ScriptConfig = z
|
|
17
|
+
.object({
|
|
18
|
+
src: z.string(),
|
|
19
|
+
async: z.boolean().optional(),
|
|
20
|
+
crossorigin: z.string().optional(),
|
|
21
|
+
defer: z.boolean().optional(),
|
|
22
|
+
fetchpriority: z.string().optional(),
|
|
23
|
+
integrity: z.string().optional(),
|
|
24
|
+
module: z.boolean().optional(),
|
|
25
|
+
nomodule: z.boolean().optional(),
|
|
26
|
+
nonce: z.string().optional(),
|
|
27
|
+
referrerpolicy: z.string().optional(),
|
|
28
|
+
type: z.string().optional(),
|
|
29
|
+
})
|
|
30
|
+
.passthrough();
|
|
31
|
+
|
|
32
|
+
const LinksConfig = z
|
|
33
|
+
.object({
|
|
34
|
+
href: z.string(),
|
|
35
|
+
|
|
36
|
+
as: z.string().optional(),
|
|
37
|
+
crossorigin: z.string().optional(),
|
|
38
|
+
fetchpriority: z.string().optional(),
|
|
39
|
+
hreflang: z.string().optional(),
|
|
40
|
+
imagesizes: z.string().optional(),
|
|
41
|
+
imagesrcset: z.string().optional(),
|
|
42
|
+
integrity: z.string().optional(),
|
|
43
|
+
media: z.string().optional(),
|
|
44
|
+
prefetch: z.string().optional(),
|
|
45
|
+
referrerpolicy: z.string().optional(),
|
|
46
|
+
rel: z.string().optional(),
|
|
47
|
+
sizes: z.string().optional(),
|
|
48
|
+
title: z.string().optional(),
|
|
49
|
+
type: z.string().optional(),
|
|
50
|
+
})
|
|
51
|
+
.passthrough();
|
|
52
|
+
|
|
16
53
|
export const ThemeConfig = z
|
|
17
54
|
.object({
|
|
18
55
|
imports: z.array(z.string()).default([]).optional(),
|
|
19
56
|
|
|
20
57
|
logo: LogoConfig.optional(),
|
|
21
|
-
navbar:
|
|
22
|
-
|
|
58
|
+
navbar: z
|
|
59
|
+
.object({
|
|
60
|
+
items: z.array(z.any()).optional(), // todo: think about validation here
|
|
61
|
+
})
|
|
62
|
+
.extend(HideConfig.shape)
|
|
63
|
+
.strict()
|
|
64
|
+
.optional(),
|
|
65
|
+
|
|
66
|
+
footer: z
|
|
67
|
+
.object({
|
|
68
|
+
items: z.array(z.any()).optional(), // todo: think about validation here
|
|
69
|
+
copyrightText: z.string().optional(),
|
|
70
|
+
})
|
|
71
|
+
.extend(HideConfig.shape)
|
|
72
|
+
.strict()
|
|
73
|
+
.optional(),
|
|
23
74
|
sidebar: HideConfig.strict().optional(),
|
|
24
|
-
navbarItems: z.any().optional(), // todo: think about validation here
|
|
25
|
-
footerItems: z.any().optional(), // todo: think about validation here
|
|
26
75
|
|
|
27
76
|
scripts: z
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
77
|
+
.object({
|
|
78
|
+
head: z.array(ScriptConfig).optional(),
|
|
79
|
+
body: z.array(ScriptConfig).optional(),
|
|
80
|
+
})
|
|
32
81
|
.optional(),
|
|
33
|
-
|
|
82
|
+
links: z.array(LinksConfig).optional(),
|
|
34
83
|
|
|
35
84
|
search: z
|
|
36
85
|
.object({
|
|
@@ -43,8 +92,7 @@ export const ThemeConfig = z
|
|
|
43
92
|
|
|
44
93
|
colorMode: z
|
|
45
94
|
.object({
|
|
46
|
-
|
|
47
|
-
default: z.string().optional(),
|
|
95
|
+
ignoreDetection: z.boolean().optional(),
|
|
48
96
|
modes: z.array(z.string()).default(['light', 'dark']).optional(),
|
|
49
97
|
})
|
|
50
98
|
.extend(HideConfig.shape)
|
|
@@ -53,13 +101,13 @@ export const ThemeConfig = z
|
|
|
53
101
|
.default({}),
|
|
54
102
|
navigation: z
|
|
55
103
|
.object({
|
|
56
|
-
|
|
57
|
-
.object({
|
|
104
|
+
nextButton: z
|
|
105
|
+
.object({ text: z.string().default('Next to {label}') })
|
|
58
106
|
.extend(HideConfig.shape)
|
|
59
107
|
.optional()
|
|
60
108
|
.default({}),
|
|
61
|
-
|
|
62
|
-
.object({
|
|
109
|
+
previousButton: z
|
|
110
|
+
.object({ text: z.string().default('Back to {label}') })
|
|
63
111
|
.extend(HideConfig.shape)
|
|
64
112
|
.optional()
|
|
65
113
|
.default({}),
|
|
@@ -69,7 +117,7 @@ export const ThemeConfig = z
|
|
|
69
117
|
.default({}),
|
|
70
118
|
markdown: z
|
|
71
119
|
.object({
|
|
72
|
-
|
|
120
|
+
frontMatterKeysToResolve: z.array(z.string()).default(['image', 'links']).optional(),
|
|
73
121
|
lastUpdatedBlock: z
|
|
74
122
|
.object({
|
|
75
123
|
format: z.enum(['timeago', 'iso', 'long', 'short']).default('timeago').optional(),
|
|
@@ -81,7 +129,7 @@ export const ThemeConfig = z
|
|
|
81
129
|
toc: z
|
|
82
130
|
.object({
|
|
83
131
|
header: z.string().default('On this page').optional(),
|
|
84
|
-
|
|
132
|
+
depth: z.number().default(3).optional(),
|
|
85
133
|
})
|
|
86
134
|
.extend(HideConfig.shape)
|
|
87
135
|
.optional()
|
|
@@ -116,15 +164,16 @@ export const ThemeConfig = z
|
|
|
116
164
|
.default({});
|
|
117
165
|
|
|
118
166
|
export type ThemeConfig = z.infer<typeof ThemeConfig>;
|
|
119
|
-
export type ThemeUIConfig = Omit<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
> & {
|
|
123
|
-
navbarItems?: any; // TODO
|
|
124
|
-
footerItems?: any; // TODO
|
|
167
|
+
export type ThemeUIConfig = Omit<ThemeConfig, 'navbar' | 'footer' | 'links' | 'scripts'> & {
|
|
168
|
+
navbar?: any; // TODO
|
|
169
|
+
footer?: any; // TODO
|
|
125
170
|
auth?: {
|
|
126
171
|
// used by portal dev login emulator
|
|
127
|
-
|
|
172
|
+
idpsInfo?: {
|
|
173
|
+
idpId: string;
|
|
174
|
+
type: string; // AuthProviderType
|
|
175
|
+
title: string | undefined;
|
|
176
|
+
}[];
|
|
128
177
|
devLogin?: boolean;
|
|
129
178
|
};
|
|
130
179
|
};
|