@redocly/theme 0.5.0 → 0.6.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.
- package/lib/ColorModeSwitcher/ColorModeSwitcher.js +23 -10
- package/lib/EditPageButton/EditPageButton.d.ts +1 -1
- package/lib/EditPageButton/EditPageButton.js +1 -1
- package/lib/Footer/Footer.js +2 -2
- package/lib/LastUpdated/LastUpdated.js +30 -7
- package/lib/Markdown/CodeSample/CodeSample.js +6 -14
- package/lib/Markdown/MarkdownLayout.d.ts +1 -1
- package/lib/Markdown/MarkdownLayout.js +5 -1
- package/lib/Navbar/Navbar.js +2 -2
- package/lib/PageNavigation/NextPageLink.js +30 -7
- package/lib/PageNavigation/PageNavigation.js +4 -3
- package/lib/PageNavigation/PreviousPageLink.js +4 -4
- package/lib/Sidebar/SidebarLayout.js +2 -2
- package/lib/Sidebar/types/MenuStyle.js +0 -1
- package/lib/TableOfContent/TableOfContent.d.ts +0 -1
- package/lib/TableOfContent/TableOfContent.js +5 -7
- package/lib/config.d.ts +385 -0
- package/lib/config.js +113 -0
- package/lib/globalStyle.js +3 -5
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/useActiveHeading.js +1 -1
- package/lib/hooks/useActiveSectionId.js +1 -1
- package/lib/hooks/useThemeConfig.d.ts +1 -0
- package/lib/hooks/useThemeConfig.js +6 -0
- package/lib/icons/ColorModeIcon/ColorModeIcon.js +3 -3
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/mocks/Link.js +1 -1
- package/lib/mocks/hooks/index.d.ts +2 -5
- package/lib/mocks/hooks/index.js +22 -6
- package/lib/mocks/types.d.ts +0 -11
- package/lib/mocks/types.js +1 -0
- package/lib/mocks/utils.js +1 -1
- package/lib/types/config.d.ts +5 -0
- package/lib/types/config.js +3 -0
- package/lib/utils/args-typecheck.js +1 -1
- package/package.json +10 -8
- package/src/ColorModeSwitcher/ColorModeSwitcher.tsx +29 -12
- package/src/EditPageButton/EditPageButton.tsx +2 -2
- package/src/Footer/Footer.tsx +2 -2
- package/src/LastUpdated/LastUpdated.tsx +8 -6
- package/src/Markdown/CodeSample/CodeSample.tsx +7 -16
- package/src/Markdown/MarkdownLayout.tsx +9 -3
- package/src/Navbar/Navbar.tsx +2 -2
- package/src/PageNavigation/NextPageLink.tsx +8 -5
- package/src/PageNavigation/PageNavigation.tsx +3 -3
- package/src/PageNavigation/PreviousPageLink.tsx +7 -4
- package/src/Sidebar/SidebarLayout.tsx +2 -2
- package/src/Sidebar/types/MenuStyle.ts +0 -1
- package/src/TableOfContent/TableOfContent.tsx +5 -7
- package/src/config.ts +130 -0
- package/src/globalStyle.ts +3 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useActiveHeading.ts +3 -1
- package/src/hooks/useActiveSectionId.ts +1 -1
- package/src/hooks/useThemeConfig.ts +1 -0
- package/src/icons/ColorModeIcon/ColorModeIcon.tsx +3 -3
- package/src/index.ts +3 -0
- package/src/mocks/Link.tsx +8 -1
- package/src/mocks/hooks/index.ts +22 -9
- package/src/mocks/types.ts +2 -11
- package/src/mocks/utils.ts +1 -1
- package/src/types/config.ts +5 -0
- package/src/types/portal/src/shared/constants.d.ts +0 -1
- package/src/utils/args-typecheck.ts +1 -1
- package/lib/hooks/useDefaultThemeSettings.d.ts +0 -2
- package/lib/hooks/useDefaultThemeSettings.js +0 -10
- package/src/hooks/useDefaultThemeSettings.ts +0 -7
package/lib/mocks/hooks/index.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useSidebarSiblingsData = exports.
|
|
4
|
-
function
|
|
3
|
+
exports.useSidebarSiblingsData = exports.useThemeConfig = void 0;
|
|
4
|
+
function useThemeConfig() {
|
|
5
5
|
return {
|
|
6
|
-
|
|
6
|
+
search: { hide: false, placement: 'navbar' },
|
|
7
|
+
markdown: {
|
|
8
|
+
toc: { maxDepth: 3, header: 'Table of contents', hide: false },
|
|
9
|
+
lastUpdatedBlock: { hide: false, format: 'timeago', locale: 'en-US' },
|
|
10
|
+
copyCodeSnippet: {
|
|
11
|
+
hide: false,
|
|
12
|
+
buttonText: 'Copy',
|
|
13
|
+
tooltipText: 'Copy to clipboard',
|
|
14
|
+
toasterText: 'Copied',
|
|
15
|
+
toasterDuration: 1500,
|
|
16
|
+
},
|
|
17
|
+
editPage: {
|
|
18
|
+
baseUrl: '',
|
|
19
|
+
text: 'Edit this page',
|
|
20
|
+
},
|
|
21
|
+
frontmatterKeysToResolve: ['image', 'links'],
|
|
22
|
+
},
|
|
7
23
|
navigation: {
|
|
8
|
-
nextPageLink: { label: 'next page theme
|
|
9
|
-
prevPageLink: { label: 'prev page theme
|
|
24
|
+
nextPageLink: { label: 'next page theme config label' },
|
|
25
|
+
prevPageLink: { label: 'prev page theme config label' },
|
|
10
26
|
},
|
|
11
27
|
colorMode: {
|
|
12
28
|
modes: ['light', 'dark'],
|
|
@@ -14,7 +30,7 @@ function useThemeSettings(_) {
|
|
|
14
30
|
},
|
|
15
31
|
};
|
|
16
32
|
}
|
|
17
|
-
exports.
|
|
33
|
+
exports.useThemeConfig = useThemeConfig;
|
|
18
34
|
function useSidebarSiblingsData() {
|
|
19
35
|
return {
|
|
20
36
|
nextPage: {
|
package/lib/mocks/types.d.ts
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
1
|
export declare type ActiveItem<T> = T;
|
|
2
2
|
export declare type SearchDocument = any;
|
|
3
3
|
export declare type OperationParameter = any;
|
|
4
|
-
export interface RawTheme {
|
|
5
|
-
name: string;
|
|
6
|
-
settings: {
|
|
7
|
-
lastUpdatedBlock?: {
|
|
8
|
-
hide?: boolean;
|
|
9
|
-
format?: 'timeago' | 'iso' | 'short' | 'long';
|
|
10
|
-
locale?: string;
|
|
11
|
-
};
|
|
12
|
-
[k: string]: any;
|
|
13
|
-
};
|
|
14
|
-
}
|
package/lib/mocks/types.js
CHANGED
package/lib/mocks/utils.js
CHANGED
|
@@ -6,7 +6,7 @@ function withPathPrefix(link) {
|
|
|
6
6
|
}
|
|
7
7
|
exports.withPathPrefix = withPathPrefix;
|
|
8
8
|
function timeAgo(lastModified) {
|
|
9
|
-
// should return format(lastModified) in
|
|
9
|
+
// should return format(lastModified) in portal
|
|
10
10
|
const d = new Date(lastModified);
|
|
11
11
|
return `${d.getDate()}-${d.getMonth() + 1}-${d.getFullYear()}`;
|
|
12
12
|
}
|
|
@@ -8,7 +8,7 @@ function isEmptyArray(items) {
|
|
|
8
8
|
exports.isEmptyArray = isEmptyArray;
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
10
10
|
function isPrimitive(arg) {
|
|
11
|
-
return ['string', 'boolean', 'number'].includes(typeof arg);
|
|
11
|
+
return ['string', 'boolean', 'number', 'undefined'].includes(typeof arg);
|
|
12
12
|
}
|
|
13
13
|
exports.isPrimitive = isPrimitive;
|
|
14
14
|
//# sourceMappingURL=args-typecheck.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Shared UI components",
|
|
5
5
|
"author": "team@redocly.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"types": "lib/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
"./package.json": "./package.json",
|
|
11
|
+
"./config.js": "./lib/config.js",
|
|
12
|
+
"./config": "./lib/config.js",
|
|
11
13
|
".": "./lib/index.js",
|
|
12
14
|
"./Sidebar/*": "./lib/Sidebar/*.js",
|
|
13
15
|
"./*": "./lib/*/index.js",
|
|
@@ -39,7 +41,8 @@
|
|
|
39
41
|
"react-dom": "^17.0.2",
|
|
40
42
|
"react-router-dom": "^5.3.0",
|
|
41
43
|
"styled-components": "^5.3.6",
|
|
42
|
-
"styled-system": "^5.1.5"
|
|
44
|
+
"styled-system": "^5.1.5",
|
|
45
|
+
"zod": ">=3.19.1"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@storybook/addon-actions": "^6.5.9",
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
"@testing-library/react": "^12.1.4",
|
|
60
63
|
"@testing-library/react-hooks": "^8.0.1",
|
|
61
64
|
"@testing-library/user-event": "^13.5.0",
|
|
62
|
-
"@types/jest": "^29.1
|
|
65
|
+
"@types/jest": "^29.2.1",
|
|
63
66
|
"@types/jest-when": "^3.5.2",
|
|
64
67
|
"@types/lodash.throttle": "^4.1.7",
|
|
65
68
|
"@types/node": "^16.11.26",
|
|
@@ -73,14 +76,12 @@
|
|
|
73
76
|
"@typescript-eslint/parser": "^5.23.0",
|
|
74
77
|
"chromatic": "^6.10.2",
|
|
75
78
|
"esbuild": "^0.15.11",
|
|
76
|
-
"jest": "^29.2.
|
|
77
|
-
"jest-environment-jsdom": "^29.2.
|
|
79
|
+
"jest": "^29.2.2",
|
|
80
|
+
"jest-environment-jsdom": "^29.2.2",
|
|
78
81
|
"jest-styled-components": "^7.1.1",
|
|
79
82
|
"jest-when": "^3.5.1",
|
|
80
83
|
"lodash.throttle": "^4.1.1",
|
|
81
84
|
"npm-run-all": "^4.1.5",
|
|
82
|
-
"react": "^17.0.2",
|
|
83
|
-
"react-dom": "^17.0.2",
|
|
84
85
|
"react-refresh": "^0.14.0",
|
|
85
86
|
"react-router-dom": "^5.3.0",
|
|
86
87
|
"storybook-addon-pseudo-states": "^1.15.1",
|
|
@@ -94,7 +95,8 @@
|
|
|
94
95
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
|
95
96
|
"typescript": "^4.8.4",
|
|
96
97
|
"webpack": "^5.72.0",
|
|
97
|
-
"concurrently": "^7.4.0"
|
|
98
|
+
"concurrently": "^7.4.0",
|
|
99
|
+
"zod": ">=3.19.1"
|
|
98
100
|
},
|
|
99
101
|
"dependencies": {
|
|
100
102
|
"timeago.js": "^4.0.2"
|
|
@@ -2,28 +2,26 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { ColorModeIcon } from '@theme/icons/ColorModeIcon';
|
|
5
|
-
import { useMount } from '@theme/hooks';
|
|
6
|
-
import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
|
|
5
|
+
import { useMount, useThemeConfig } from '@theme/hooks';
|
|
7
6
|
|
|
8
7
|
export function ColorModeSwitcher(): JSX.Element | null {
|
|
9
|
-
const themeSettings =
|
|
8
|
+
const themeSettings = useThemeConfig();
|
|
10
9
|
const colorMode = themeSettings.colorMode;
|
|
11
10
|
const [activeColorMode, setActiveColorMode] = useState('');
|
|
11
|
+
const modes = colorMode?.modes || ['light', 'dark'];
|
|
12
|
+
const defaultColor = colorMode?.default || modes[0] || 'light';
|
|
12
13
|
|
|
13
14
|
useMount(() => {
|
|
14
|
-
setActiveColorMode(document.documentElement.className ||
|
|
15
|
+
setActiveColorMode(document.documentElement.className || defaultColor);
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
if (
|
|
18
|
+
if (colorMode?.hide) {
|
|
18
19
|
return null;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const handelChangeColorMode = () => {
|
|
22
|
-
const activeIndex =
|
|
23
|
-
const mode =
|
|
24
|
-
activeIndex < colorMode.modes.length - 1
|
|
25
|
-
? colorMode.modes[activeIndex + 1]
|
|
26
|
-
: colorMode.modes[0];
|
|
23
|
+
const activeIndex = modes.indexOf(activeColorMode);
|
|
24
|
+
const mode = activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0];
|
|
27
25
|
setActiveColorMode(mode);
|
|
28
26
|
localStorage.setItem('colorSchema', mode);
|
|
29
27
|
document.documentElement.className = `${mode} notransition`;
|
|
@@ -37,16 +35,35 @@ export function ColorModeSwitcher(): JSX.Element | null {
|
|
|
37
35
|
<Wrapper
|
|
38
36
|
data-component-name="ColorModeSwitcher/ColorModeSwitcher"
|
|
39
37
|
onClick={handelChangeColorMode}
|
|
38
|
+
modes={modes}
|
|
40
39
|
>
|
|
41
|
-
|
|
40
|
+
{modes.map((mode) => (
|
|
41
|
+
<ColorModeIcon mode={mode} key={mode} />
|
|
42
|
+
))}
|
|
42
43
|
</Wrapper>
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
const Wrapper = styled.div
|
|
47
|
+
const Wrapper = styled.div<{ modes: string[] }>`
|
|
47
48
|
margin-left: var(--navbar-item-padding-horizontal);
|
|
48
49
|
display: flex;
|
|
49
50
|
align-items: center;
|
|
50
51
|
cursor: pointer;
|
|
51
52
|
user-select: none;
|
|
53
|
+
|
|
54
|
+
${ColorModeIcon} {
|
|
55
|
+
display: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
${({ modes }: { modes: string[] }) => {
|
|
59
|
+
const items = modes.map((mode) => {
|
|
60
|
+
return `
|
|
61
|
+
html.${mode} & ${ColorModeIcon}.${mode} {
|
|
62
|
+
display: block;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return items.join('');
|
|
68
|
+
}}
|
|
52
69
|
`;
|
|
@@ -6,13 +6,13 @@ import { Link } from '@portal/Link';
|
|
|
6
6
|
export interface EditPageButtonProps {
|
|
7
7
|
text: string;
|
|
8
8
|
to: string;
|
|
9
|
-
icon
|
|
9
|
+
icon?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const EditPageButton = ({ text, to, icon }: EditPageButtonProps): JSX.Element => {
|
|
13
13
|
return (
|
|
14
14
|
<EditButton to={to}>
|
|
15
|
-
<ButtonIcon src={icon} />
|
|
15
|
+
{icon ? <ButtonIcon src={icon} /> : null}
|
|
16
16
|
<ButtonText>{text}</ButtonText>
|
|
17
17
|
</EditButton>
|
|
18
18
|
);
|
package/src/Footer/Footer.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import styled from 'styled-components';
|
|
|
4
4
|
import { FooterColumns } from '@theme/Footer/FooterColumns';
|
|
5
5
|
import { FooterCopyright } from '@theme/Footer/FooterCopyright';
|
|
6
6
|
import { isEmptyArray } from '@theme/utils';
|
|
7
|
-
import {
|
|
7
|
+
import { useThemeConfig } from '@theme/hooks';
|
|
8
8
|
import type { NavGroupRecord, ResolvedNavItem } from '@theme/types/portal';
|
|
9
9
|
|
|
10
10
|
interface FooterProps {
|
|
@@ -12,7 +12,7 @@ interface FooterProps {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function Footer({ data: { columns, copyrightText } }: FooterProps): JSX.Element | null {
|
|
15
|
-
const { footer } =
|
|
15
|
+
const { footer } = useThemeConfig();
|
|
16
16
|
|
|
17
17
|
if (isEmptyArray(columns) || !copyrightText || footer?.hide) {
|
|
18
18
|
return null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { format } from 'timeago.js';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
6
6
|
|
|
7
7
|
const FORMATS = {
|
|
8
8
|
timeago: (date: Date, locale: string) => format(date, locale),
|
|
@@ -20,18 +20,19 @@ export interface LastUpdatedProps {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
|
|
23
|
-
const { lastUpdatedBlock } =
|
|
23
|
+
const { markdown: { lastUpdatedBlock = {} } = {} } = useThemeConfig();
|
|
24
24
|
|
|
25
25
|
if (lastUpdatedBlock?.hide) {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const lastModified = props.lastModified;
|
|
30
|
-
const format = props.format || lastUpdatedBlock
|
|
31
|
-
const locale = props.locale || lastUpdatedBlock
|
|
30
|
+
const format = props.format || lastUpdatedBlock.format || 'timeago';
|
|
31
|
+
const locale = props.locale || lastUpdatedBlock.locale || 'en-US';
|
|
32
32
|
const isoDate = lastModified.toISOString().split('T')[0];
|
|
33
33
|
|
|
34
|
-
const lastUpdatedString = FORMATS[format](lastModified, locale);
|
|
34
|
+
const lastUpdatedString = FORMATS[format as keyof typeof FORMATS](lastModified, locale);
|
|
35
|
+
|
|
35
36
|
const separator = format === 'timeago' ? ' ' : ' on ';
|
|
36
37
|
|
|
37
38
|
return (
|
|
@@ -41,6 +42,7 @@ export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
|
|
|
41
42
|
data-print-datetime={isoDate}
|
|
42
43
|
>
|
|
43
44
|
Last updated{separator}
|
|
45
|
+
{/* TODO: fix issue with snapshot tests - they should not depend on current date */}
|
|
44
46
|
<time dateTime={isoDate}>{lastUpdatedString}</time>
|
|
45
47
|
</Wrapper>
|
|
46
48
|
);
|
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import styled, { css } from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { ClipboardService } from '@theme/utils/ClipboardService';
|
|
5
|
-
import {
|
|
5
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
6
6
|
|
|
7
7
|
export type CodeSampleProps = {
|
|
8
8
|
language: string;
|
|
@@ -10,37 +10,28 @@ export type CodeSampleProps = {
|
|
|
10
10
|
rawContent: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
const defaultCopyCodeSnippet = {
|
|
14
|
-
hide: false,
|
|
15
|
-
buttonText: 'Copy',
|
|
16
|
-
tooltipText: 'Copy the code snippet',
|
|
17
|
-
toasterText: 'Copied',
|
|
18
|
-
toasterDuration: 1500,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
13
|
export function CodeSample({ rawContent, highlighted, language }: CodeSampleProps): JSX.Element {
|
|
22
14
|
const langClassName = language ? `language-${language}` : '';
|
|
23
|
-
const { copyCodeSnippet } =
|
|
24
|
-
const copyCodeProps = { ...defaultCopyCodeSnippet, ...copyCodeSnippet };
|
|
15
|
+
const { markdown: { copyCodeSnippet = {} } = {} } = useThemeConfig();
|
|
25
16
|
|
|
26
17
|
const [isCopied, setIsCopied] = useState(false);
|
|
27
18
|
|
|
28
19
|
const copyCode = (code: string) => {
|
|
29
20
|
ClipboardService.copyCustom(code);
|
|
30
21
|
setIsCopied(true);
|
|
31
|
-
setTimeout(() => setIsCopied(false),
|
|
22
|
+
setTimeout(() => setIsCopied(false), copyCodeSnippet.toasterDuration);
|
|
32
23
|
};
|
|
33
24
|
|
|
34
25
|
return (
|
|
35
26
|
<Wrapper className="code-sample" data-component-name="Markdown/CodeSample/CodeSample">
|
|
36
|
-
{!
|
|
27
|
+
{!copyCodeSnippet.hide && (
|
|
37
28
|
<CodeSampleButtonContainer onClick={() => copyCode(rawContent)}>
|
|
38
29
|
{!isCopied && (
|
|
39
|
-
<CopyCodeButton title={
|
|
40
|
-
{
|
|
30
|
+
<CopyCodeButton title={copyCodeSnippet.tooltipText || 'Copy to clipboard'}>
|
|
31
|
+
{copyCodeSnippet.buttonText}
|
|
41
32
|
</CopyCodeButton>
|
|
42
33
|
)}
|
|
43
|
-
{isCopied && <DoneIndicator>{
|
|
34
|
+
{isCopied && <DoneIndicator>{copyCodeSnippet.toasterText}</DoneIndicator>}
|
|
44
35
|
</CodeSampleButtonContainer>
|
|
45
36
|
)}
|
|
46
37
|
<pre className={langClassName}>
|
|
@@ -6,6 +6,7 @@ import { ContainerWrapper } from '@theme/Markdown/ContainerWrapper';
|
|
|
6
6
|
import { PageNavigation } from '@theme/PageNavigation/PageNavigation';
|
|
7
7
|
import { EditPageButton } from '@theme/EditPageButton';
|
|
8
8
|
import { LastUpdated } from '@theme/LastUpdated/LastUpdated';
|
|
9
|
+
import { useThemeConfig } from '@theme/hooks';
|
|
9
10
|
|
|
10
11
|
type MarkdownLayoutProps = {
|
|
11
12
|
tableOfContent: React.ReactNode;
|
|
@@ -13,7 +14,7 @@ type MarkdownLayoutProps = {
|
|
|
13
14
|
editPage?: {
|
|
14
15
|
to: string;
|
|
15
16
|
text: string;
|
|
16
|
-
icon
|
|
17
|
+
icon?: string;
|
|
17
18
|
};
|
|
18
19
|
/** String in ISO format */
|
|
19
20
|
lastModified?: string | null;
|
|
@@ -25,13 +26,18 @@ export function MarkdownLayout({
|
|
|
25
26
|
editPage,
|
|
26
27
|
lastModified,
|
|
27
28
|
}: MarkdownLayoutProps): JSX.Element {
|
|
29
|
+
const { markdown } = useThemeConfig();
|
|
30
|
+
const { editPage: themeEditPage } = markdown || {};
|
|
31
|
+
|
|
32
|
+
const mergedConf = editPage ? { ...editPage, ...themeEditPage } : undefined;
|
|
33
|
+
|
|
28
34
|
return (
|
|
29
35
|
<PageWrapper data-component-name="Markdown/MarkdownLayout">
|
|
30
36
|
<ContainerWrapper withToc={true}>
|
|
31
37
|
<LayoutTop>
|
|
32
38
|
{lastModified && <LastUpdated lastModified={new Date(lastModified)} />}
|
|
33
|
-
{
|
|
34
|
-
<EditPageButton text={
|
|
39
|
+
{mergedConf && (
|
|
40
|
+
<EditPageButton text={mergedConf.text} to={mergedConf.to} icon={mergedConf.icon} />
|
|
35
41
|
)}
|
|
36
42
|
</LayoutTop>
|
|
37
43
|
{markdownWrapper}
|
package/src/Navbar/Navbar.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { useMobileMenu } from '@theme/hooks/useMobileMenu';
|
|
|
6
6
|
import { MobileNavbarMenuButton } from '@theme/Navbar/MobileNavbarMenuButton';
|
|
7
7
|
import { MobileNavbarMenu } from '@theme/Navbar/MobileNavbarMenu';
|
|
8
8
|
import { ColorModeSwitcher } from '@theme/ColorModeSwitcher/ColorModeSwitcher';
|
|
9
|
-
import {
|
|
9
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
10
10
|
import type { ResolvedConfigLinks } from '@theme/types/portal';
|
|
11
11
|
|
|
12
12
|
interface NavbarProps {
|
|
@@ -18,7 +18,7 @@ interface NavbarProps {
|
|
|
18
18
|
|
|
19
19
|
export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Element | null {
|
|
20
20
|
const [isOpen, setIsOpen] = useMobileMenu(false);
|
|
21
|
-
const { search: searchSettings, navbar } =
|
|
21
|
+
const { search: searchSettings, navbar } = useThemeConfig();
|
|
22
22
|
const hideSearch =
|
|
23
23
|
searchSettings?.hide || (searchSettings?.placement && searchSettings?.placement !== 'navbar');
|
|
24
24
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { useSidebarSiblingsData } from '@portal/hooks';
|
|
5
5
|
import { Button } from '@theme/Button/Button';
|
|
6
|
-
import {
|
|
6
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
7
7
|
import type { ResolvedNavItemWithLink } from '@theme/types/portal';
|
|
8
8
|
|
|
9
9
|
interface NextPageType {
|
|
@@ -12,13 +12,16 @@ interface NextPageType {
|
|
|
12
12
|
|
|
13
13
|
export function NextPageLink(): JSX.Element {
|
|
14
14
|
const { nextPage }: NextPageType = useSidebarSiblingsData() || {};
|
|
15
|
-
const { navigation } =
|
|
15
|
+
const { navigation } = useThemeConfig();
|
|
16
16
|
|
|
17
|
-
if (!nextPage || navigation?.
|
|
17
|
+
if (!nextPage || navigation?.nextPageLink?.hide) {
|
|
18
18
|
return <div> </div>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const label = navigation?.nextPageLink?.label ||
|
|
21
|
+
const label = (navigation?.nextPageLink?.label || 'Next to {label}').replace(
|
|
22
|
+
'{label}',
|
|
23
|
+
nextPage.label || nextPage.routeSlug || '',
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
27
|
<StyledButton
|
|
@@ -3,12 +3,12 @@ import styled from 'styled-components';
|
|
|
3
3
|
|
|
4
4
|
import { PreviousPageLink } from '@theme/PageNavigation/PreviousPageLink';
|
|
5
5
|
import { NextPageLink } from '@theme/PageNavigation/NextPageLink';
|
|
6
|
-
import {
|
|
6
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
7
7
|
|
|
8
8
|
export function PageNavigation(): JSX.Element | null {
|
|
9
|
-
const { navigation } =
|
|
9
|
+
const { navigation } = useThemeConfig();
|
|
10
10
|
|
|
11
|
-
if (navigation?.hide) {
|
|
11
|
+
if (navigation?.prevPageLink?.hide && navigation?.nextPageLink?.hide) {
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { useSidebarSiblingsData } from '@portal/hooks';
|
|
5
|
-
import {
|
|
5
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
6
6
|
import { Button } from '@theme/Button/Button';
|
|
7
7
|
import type { ResolvedNavItemWithLink } from '@theme/types/portal';
|
|
8
8
|
|
|
@@ -12,13 +12,16 @@ interface PreviousPageType {
|
|
|
12
12
|
|
|
13
13
|
export function PreviousPageLink(): JSX.Element {
|
|
14
14
|
const { prevPage }: PreviousPageType = useSidebarSiblingsData() || {};
|
|
15
|
-
const { navigation } =
|
|
15
|
+
const { navigation } = useThemeConfig();
|
|
16
16
|
|
|
17
|
-
if (!prevPage || navigation?.
|
|
17
|
+
if (!prevPage || navigation?.prevPageLink?.hide) {
|
|
18
18
|
return <div> </div>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const label = navigation?.prevPageLink?.label ||
|
|
21
|
+
const label = (navigation?.prevPageLink?.label || 'Back to {label}').replace(
|
|
22
|
+
'{label}',
|
|
23
|
+
prevPage.label || prevPage.routeSlug || '',
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
27
|
<StyledButton
|
|
@@ -6,7 +6,7 @@ import { useMobileMenu } from '@theme/hooks/useMobileMenu';
|
|
|
6
6
|
import { MobileSidebarButton } from '@theme/Sidebar/MobileSidebarButton';
|
|
7
7
|
import { MenuContainer } from '@theme/Sidebar/MenuContainer';
|
|
8
8
|
import { SidebarSearch } from '@theme/Search/SidebarSearch';
|
|
9
|
-
import {
|
|
9
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
10
10
|
|
|
11
11
|
interface SidebarLayoutProps {
|
|
12
12
|
versions: React.ReactNode;
|
|
@@ -16,7 +16,7 @@ interface SidebarLayoutProps {
|
|
|
16
16
|
export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Element | null {
|
|
17
17
|
const [isOpen, setIsOpen] = useMobileMenu();
|
|
18
18
|
const toggleMenu = () => setIsOpen(!isOpen);
|
|
19
|
-
const { search, sidebar } =
|
|
19
|
+
const { search, sidebar } = useThemeConfig();
|
|
20
20
|
|
|
21
21
|
if (sidebar?.hide) {
|
|
22
22
|
return null;
|
|
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
|
|
3
3
|
|
|
4
4
|
import { useFullHeight } from '@theme/hooks/useFullHeight';
|
|
5
5
|
import { useActiveHeading } from '@theme/hooks/useActiveHeading';
|
|
6
|
-
import {
|
|
6
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
7
7
|
import type { MdHeading } from '@theme/types/portal';
|
|
8
8
|
|
|
9
9
|
import { getDisplayedHeadingsIds, getDisplayedHeadings, getLeastDepth } from './utils';
|
|
@@ -11,17 +11,16 @@ import { getDisplayedHeadingsIds, getDisplayedHeadings, getLeastDepth } from './
|
|
|
11
11
|
interface TableOfContentProps {
|
|
12
12
|
headings?: Array<MdHeading | null> | null | undefined;
|
|
13
13
|
contentWrapper: HTMLDivElement | null;
|
|
14
|
-
tocMaxDepth: number;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
|
|
18
|
-
const { headings,
|
|
17
|
+
const { headings, contentWrapper } = props;
|
|
19
18
|
|
|
20
19
|
const sidebar = useRef<HTMLDivElement | null>(null);
|
|
21
20
|
useFullHeight(sidebar);
|
|
22
|
-
const { toc } =
|
|
21
|
+
const { markdown: { toc = {} } = {} } = useThemeConfig();
|
|
23
22
|
|
|
24
|
-
const displayedHeadings = getDisplayedHeadings(headings,
|
|
23
|
+
const displayedHeadings = getDisplayedHeadings(headings, toc.maxDepth || 3);
|
|
25
24
|
const leastDepth = getLeastDepth(displayedHeadings);
|
|
26
25
|
|
|
27
26
|
const activeHeadingId = useActiveHeading(
|
|
@@ -44,9 +43,8 @@ export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
|
|
|
44
43
|
{headings && (
|
|
45
44
|
<TableOfContentMenu data-component-name="TableOfContent/TableOfContent">
|
|
46
45
|
<TableOfContentItems ref={sidebar}>
|
|
47
|
-
<TocHeader>{toc
|
|
46
|
+
<TocHeader>{toc.header || 'On this page'}</TocHeader>
|
|
48
47
|
{displayedHeadings.map((heading: MdHeading | null, idx: number) => {
|
|
49
|
-
// TODO: not sure about !heading
|
|
50
48
|
if (!heading) {
|
|
51
49
|
return null;
|
|
52
50
|
}
|