@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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/lib/ColorModeSwitcher/ColorModeSwitcher.js +1 -1
  3. package/lib/Footer/Footer.d.ts +6 -3
  4. package/lib/Footer/Footer.js +3 -3
  5. package/lib/PageNavigation/NextButton.d.ts +2 -0
  6. package/lib/PageNavigation/{NextPageLink.js → NextButton.js} +7 -7
  7. package/lib/PageNavigation/PageNavigation.js +5 -5
  8. package/lib/PageNavigation/PreviousButton.d.ts +2 -0
  9. package/lib/PageNavigation/{PreviousPageLink.js → PreviousButton.js} +7 -7
  10. package/lib/PageNavigation/index.d.ts +2 -2
  11. package/lib/PageNavigation/index.js +2 -2
  12. package/lib/Sidebar/ArrowBack.js +2 -2
  13. package/lib/Sidebar/SidebarLayout.d.ts +5 -1
  14. package/lib/Sidebar/SidebarLayout.js +26 -1
  15. package/lib/Sidebar/types/NavItem.d.ts +1 -1
  16. package/lib/TableOfContent/TableOfContent.js +14 -19
  17. package/lib/TableOfContent/utils.d.ts +1 -1
  18. package/lib/TableOfContent/utils.js +2 -2
  19. package/lib/config.d.ts +342 -72
  20. package/lib/config.js +61 -17
  21. package/lib/globalStyle.js +1 -1
  22. package/lib/mocks/hooks/index.js +4 -5
  23. package/package.json +3 -3
  24. package/src/ColorModeSwitcher/ColorModeSwitcher.tsx +1 -1
  25. package/src/Footer/Footer.tsx +8 -5
  26. package/src/PageNavigation/{NextPageLink.tsx → NextButton.tsx} +4 -4
  27. package/src/PageNavigation/PageNavigation.tsx +5 -5
  28. package/src/PageNavigation/{PreviousPageLink.tsx → PreviousButton.tsx} +4 -4
  29. package/src/PageNavigation/index.ts +2 -2
  30. package/src/Sidebar/ArrowBack.tsx +2 -2
  31. package/src/Sidebar/SidebarLayout.tsx +38 -1
  32. package/src/Sidebar/types/NavItem.ts +1 -1
  33. package/src/TableOfContent/TableOfContent.tsx +24 -32
  34. package/src/TableOfContent/utils.ts +2 -2
  35. package/src/config.ts +73 -24
  36. package/src/globalStyle.ts +1 -1
  37. package/src/mocks/hooks/index.ts +4 -5
  38. package/src/settings.yaml +2 -2
  39. package/src/types/portal/src/shared/constants.d.ts +0 -1
  40. package/src/types/portal/src/shared/types/nav.d.ts +1 -2
  41. package/lib/PageNavigation/NextPageLink.d.ts +0 -2
  42. 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: HideConfig.strict().optional(),
21
- footer: HideConfig.strict().optional(),
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
- .array(zod_1.z.union([zod_1.z.string(), zod_1.z.record(zod_1.z.union([zod_1.z.boolean(), zod_1.z.string()]))]))
27
- .optional(),
28
- postBodyScripts: zod_1.z
29
- .array(zod_1.z.union([zod_1.z.string(), zod_1.z.record(zod_1.z.union([zod_1.z.boolean(), zod_1.z.string()]))]))
71
+ .object({
72
+ head: zod_1.z.array(ScriptConfig).optional(),
73
+ body: zod_1.z.array(ScriptConfig).optional(),
74
+ })
30
75
  .optional(),
31
- styles: zod_1.z.array(zod_1.z.union([zod_1.z.string(), zod_1.z.record(zod_1.z.union([zod_1.z.boolean(), zod_1.z.string()]))])).optional(),
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
- disableDetect: zod_1.z.boolean().optional(),
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
- nextPageLink: zod_1.z
53
- .object({ label: zod_1.z.string().default('Next to {label}') })
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
- prevPageLink: zod_1.z
58
- .object({ label: zod_1.z.string().default('Back to {label}') })
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
- frontmatterKeysToResolve: zod_1.z.array(zod_1.z.string()).default(['image', 'links']).optional(),
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
- maxDepth: zod_1.z.number().default(3).optional(),
124
+ depth: zod_1.z.number().default(3).optional(),
81
125
  })
82
126
  .extend(HideConfig.shape)
83
127
  .optional()
@@ -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(--sidebar-item-text-color);
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;
@@ -5,7 +5,7 @@ function useThemeConfig() {
5
5
  return {
6
6
  search: { hide: false, placement: 'navbar' },
7
7
  markdown: {
8
- toc: { maxDepth: 3, header: 'Table of contents', hide: false },
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
- frontmatterKeysToResolve: ['image', 'links'],
21
+ frontMatterKeysToResolve: ['image', 'links'],
22
22
  },
23
23
  navigation: {
24
- nextPageLink: { label: 'next page theme config label' },
25
- prevPageLink: { label: 'prev page theme config label' },
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.0",
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.11",
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.15",
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 = colorMode?.default || modes[0] || 'light';
12
+ const defaultColor = modes[0] || 'light';
13
13
 
14
14
  useMount(() => {
15
15
  setActiveColorMode(document.documentElement.className || defaultColor);
@@ -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 { NavGroupRecord, ResolvedNavItem } from '@theme/types/portal';
8
+ import type { NavGroup, ResolvedNavItem } from '@theme/types/portal';
9
9
 
10
10
  interface FooterProps {
11
- data: NavGroupRecord;
11
+ data: {
12
+ items?: NavGroup;
13
+ copyrightText?: string;
14
+ };
12
15
  }
13
16
 
14
- export function Footer({ data: { columns, copyrightText } }: FooterProps): JSX.Element | null {
17
+ export function Footer({ data: { items, copyrightText } }: FooterProps): JSX.Element | null {
15
18
  const { footer } = useThemeConfig();
16
19
 
17
- if (isEmptyArray(columns) || !copyrightText || footer?.hide) {
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={columns as ResolvedNavItem[]} />
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 NextPageLink(): JSX.Element {
13
+ export function NextButton(): JSX.Element {
14
14
  const { nextPage }: NextPageType = useSidebarSiblingsData() || {};
15
15
  const { navigation } = useThemeConfig();
16
16
 
17
- if (!nextPage || navigation?.nextPageLink?.hide) {
17
+ if (!nextPage || navigation?.nextButton?.hide) {
18
18
  return <div>&nbsp;</div>;
19
19
  }
20
20
 
21
- const label = (navigation?.nextPageLink?.label || 'Next to {label}').replace(
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
- {label}
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 { PreviousPageLink } from '@theme/PageNavigation/PreviousPageLink';
5
- import { NextPageLink } from '@theme/PageNavigation/NextPageLink';
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?.prevPageLink?.hide && navigation?.nextPageLink?.hide) {
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
- <PreviousPageLink />
18
- <NextPageLink />
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 PreviousPageLink(): JSX.Element {
13
+ export function PreviousButton(): JSX.Element {
14
14
  const { prevPage }: PreviousPageType = useSidebarSiblingsData() || {};
15
15
  const { navigation } = useThemeConfig();
16
16
 
17
- if (!prevPage || navigation?.prevPageLink?.hide) {
17
+ if (!prevPage || navigation?.previousButton?.hide) {
18
18
  return <div>&nbsp;</div>;
19
19
  }
20
20
 
21
- const label = (navigation?.prevPageLink?.label || 'Back to {label}').replace(
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
- {label}
33
+ {text}
34
34
  </StyledButton>
35
35
  );
36
36
  }
@@ -1,3 +1,3 @@
1
- export * from '@theme/PageNavigation/NextPageLink';
1
+ export * from '@theme/PageNavigation/NextButton';
2
2
  export * from '@theme/PageNavigation/PageNavigation';
3
- export * from '@theme/PageNavigation/PreviousPageLink';
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="12px"
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) * 1.5);
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({ versions, menu }: SidebarLayoutProps): JSX.Element | null {
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``;
@@ -19,7 +19,7 @@ export interface NavItem {
19
19
  target?: string;
20
20
  external?: boolean;
21
21
  items?: NavItem[];
22
- permission?: string;
22
+ requiredPermission?: string;
23
23
  menuStyle?: MenuStyle;
24
24
  separatorLine?: boolean;
25
25
  version?: string;
@@ -1,4 +1,4 @@
1
- import React, { useRef } from 'react';
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.maxDepth || 3);
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
- {headings && (
44
- <TableOfContentMenu data-component-name="TableOfContent/TableOfContent">
45
- <TableOfContentItems ref={sidebar}>
46
- <TocHeader>{toc.header || 'On this page'}</TocHeader>
47
- {displayedHeadings.map((heading: MdHeading | null, idx: number) => {
48
- if (!heading) {
49
- return null;
50
- }
51
- const href = '#' + heading.id;
52
- return (
53
- <MenuItem
54
- key={href + idx}
55
- href={href}
56
- depth={heading.depth - leastDepth + 1 || 0}
57
- className={activeHeadingId === heading.id ? 'active' : ''}
58
- dangerouslySetInnerHTML={{ __html: heading.value || '' }}
59
- data-cy={`toc-${heading.value}`}
60
- />
61
- );
62
- })}
63
- </TableOfContentItems>
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
- tocMaxDepth: number,
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 > tocMaxDepth);
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: HideConfig.strict().optional(),
22
- footer: HideConfig.strict().optional(),
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
- .array(z.union([z.string(), z.record(z.union([z.boolean(), z.string()]))]))
29
- .optional(),
30
- postBodyScripts: z
31
- .array(z.union([z.string(), z.record(z.union([z.boolean(), z.string()]))]))
77
+ .object({
78
+ head: z.array(ScriptConfig).optional(),
79
+ body: z.array(ScriptConfig).optional(),
80
+ })
32
81
  .optional(),
33
- styles: z.array(z.union([z.string(), z.record(z.union([z.boolean(), z.string()]))])).optional(),
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
- disableDetect: z.boolean().optional(),
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
- nextPageLink: z
57
- .object({ label: z.string().default('Next to {label}') })
104
+ nextButton: z
105
+ .object({ text: z.string().default('Next to {label}') })
58
106
  .extend(HideConfig.shape)
59
107
  .optional()
60
108
  .default({}),
61
- prevPageLink: z
62
- .object({ label: z.string().default('Back to {label}') })
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
- frontmatterKeysToResolve: z.array(z.string()).default(['image', 'links']).optional(),
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
- maxDepth: z.number().default(3).optional(),
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
- ThemeConfig,
121
- 'navbarItems' | 'footerItems' | 'styles' | 'scripts' | 'postBodyScripts'
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
- idpIds?: string[];
172
+ idpsInfo?: {
173
+ idpId: string;
174
+ type: string; // AuthProviderType
175
+ title: string | undefined;
176
+ }[];
128
177
  devLogin?: boolean;
129
178
  };
130
179
  };