mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 19:46:57 +02:00
refactor(v2): add useThemeConfig hook + cleanup useless theme default values (#3394)
* refactor(theme-classic): clean default or fallback values * refactor(theme-classic): fix announcementbar undefined error * refactor(theme-classic): fixed react hook warning error * refactor(theme-classic): revert prism destruct * create useThemeConfig and use it whenever possible * validateThemeConfig => add [] as default value for almost all arrays * fix tests Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
f5f2064656
commit
0951eef2d7
16 changed files with 154 additions and 142 deletions
|
@ -11,14 +11,19 @@ const {ThemeConfigSchema, DEFAULT_CONFIG} = require('../validateThemeConfig');
|
||||||
|
|
||||||
const {normalizeThemeConfig} = require('@docusaurus/utils-validation');
|
const {normalizeThemeConfig} = require('@docusaurus/utils-validation');
|
||||||
|
|
||||||
function testValidateThemeConfig(themeConfig) {
|
function testValidateThemeConfig(partialThemeConfig) {
|
||||||
return normalizeThemeConfig(ThemeConfigSchema, themeConfig);
|
return normalizeThemeConfig(ThemeConfigSchema, {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...partialThemeConfig,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testOk(partialConfig) {
|
function testOk(partialThemeConfig) {
|
||||||
expect(testValidateThemeConfig(partialConfig)).toEqual({
|
expect(
|
||||||
|
testValidateThemeConfig({...DEFAULT_CONFIG, ...partialThemeConfig}),
|
||||||
|
).toEqual({
|
||||||
...DEFAULT_CONFIG,
|
...DEFAULT_CONFIG,
|
||||||
...partialConfig,
|
...partialThemeConfig,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +106,10 @@ describe('themeConfig', () => {
|
||||||
};
|
};
|
||||||
expect(testValidateThemeConfig(altTagConfig)).toEqual({
|
expect(testValidateThemeConfig(altTagConfig)).toEqual({
|
||||||
...DEFAULT_CONFIG,
|
...DEFAULT_CONFIG,
|
||||||
...altTagConfig,
|
navbar: {
|
||||||
|
...DEFAULT_CONFIG.navbar,
|
||||||
|
...altTagConfig.navbar,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -117,7 +125,7 @@ describe('themeConfig', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.only('customCss config', () => {
|
describe('customCss config', () => {
|
||||||
test('should accept customCss undefined', () => {
|
test('should accept customCss undefined', () => {
|
||||||
testOk({
|
testOk({
|
||||||
customCss: undefined,
|
customCss: undefined,
|
||||||
|
|
|
@ -7,21 +7,23 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';
|
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function AnnouncementBar(): JSX.Element | null {
|
function AnnouncementBar(): JSX.Element | null {
|
||||||
const {
|
|
||||||
siteConfig: {themeConfig: {announcementBar = {}} = {}} = {},
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
const {content, backgroundColor, textColor, isCloseable} = announcementBar;
|
|
||||||
const {
|
const {
|
||||||
isAnnouncementBarClosed,
|
isAnnouncementBarClosed,
|
||||||
closeAnnouncementBar,
|
closeAnnouncementBar,
|
||||||
} = useUserPreferencesContext();
|
} = useUserPreferencesContext();
|
||||||
|
const {announcementBar} = useThemeConfig();
|
||||||
|
|
||||||
|
if (!announcementBar) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {content, backgroundColor, textColor, isCloseable} = announcementBar;
|
||||||
if (!content || (isCloseable && isAnnouncementBarClosed)) {
|
if (!content || (isCloseable && isAnnouncementBarClosed)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ import clsx from 'clsx';
|
||||||
import Highlight, {defaultProps} from 'prism-react-renderer';
|
import Highlight, {defaultProps} from 'prism-react-renderer';
|
||||||
import copy from 'copy-text-to-clipboard';
|
import copy from 'copy-text-to-clipboard';
|
||||||
import rangeParser from 'parse-numeric-range';
|
import rangeParser from 'parse-numeric-range';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import usePrismTheme from '@theme/hooks/usePrismTheme';
|
import usePrismTheme from '@theme/hooks/usePrismTheme';
|
||||||
import type {Props} from '@theme/CodeBlock';
|
import type {Props} from '@theme/CodeBlock';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
|
|
||||||
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
||||||
const getHighlightDirectiveRegex = (
|
const getHighlightDirectiveRegex = (
|
||||||
|
@ -93,11 +93,7 @@ export default ({
|
||||||
className: languageClassName,
|
className: languageClassName,
|
||||||
metastring,
|
metastring,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const {
|
const {prism} = useThemeConfig();
|
||||||
siteConfig: {
|
|
||||||
themeConfig: {prism = {}},
|
|
||||||
},
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
|
|
||||||
const [showCopied, setShowCopied] = useState(false);
|
const [showCopied, setShowCopied] = useState(false);
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
} from '@theme/hooks/useDocs';
|
} from '@theme/hooks/useDocs';
|
||||||
|
|
||||||
function DocItem(props: Props): JSX.Element {
|
function DocItem(props: Props): JSX.Element {
|
||||||
const {siteConfig = {}} = useDocusaurusContext();
|
const {siteConfig} = useDocusaurusContext();
|
||||||
const {url: siteUrl, title: siteTitle, titleDelimiter} = siteConfig;
|
const {url: siteUrl, title: siteTitle, titleDelimiter} = siteConfig;
|
||||||
const {content: DocContent} = props;
|
const {content: DocContent} = props;
|
||||||
const {metadata} = DocContent;
|
const {metadata} = DocContent;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import React, {useState, useCallback, useEffect, useRef} from 'react';
|
import React, {useState, useCallback, useEffect, useRef} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';
|
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';
|
||||||
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
||||||
import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';
|
import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';
|
||||||
|
@ -171,11 +172,9 @@ function DocSidebar({
|
||||||
}: Props): JSX.Element | null {
|
}: Props): JSX.Element | null {
|
||||||
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
navbar: {title, hideOnScroll},
|
||||||
themeConfig: {navbar: {title = '', hideOnScroll = false} = {}} = {},
|
} = useThemeConfig();
|
||||||
} = {},
|
const {isClient} = useDocusaurusContext();
|
||||||
isClient,
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||||
const {isAnnouncementBarClosed} = useUserPreferencesContext();
|
const {isAnnouncementBarClosed} = useUserPreferencesContext();
|
||||||
const {scrollY} = useScrollPosition();
|
const {scrollY} = useScrollPosition();
|
||||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
@ -40,10 +40,7 @@ const FooterLogo = ({url, alt}) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
function Footer(): JSX.Element | null {
|
function Footer(): JSX.Element | null {
|
||||||
const context = useDocusaurusContext();
|
const {footer} = useThemeConfig();
|
||||||
const {siteConfig = {}} = context;
|
|
||||||
const {themeConfig = {}} = siteConfig;
|
|
||||||
const {footer} = themeConfig;
|
|
||||||
|
|
||||||
const {copyright, links = [], logo = {}} = footer || {};
|
const {copyright, links = [], logo = {}} = footer || {};
|
||||||
const logoUrl = useBaseUrl(logo.src);
|
const logoUrl = useBaseUrl(logo.src);
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import type {HeadingType, Props} from '@theme/Heading';
|
import type {HeadingType, Props} from '@theme/Heading';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
|
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
@ -18,10 +18,8 @@ import styles from './styles.module.css';
|
||||||
const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
|
const Heading = (Tag: HeadingType): ((props: Props) => JSX.Element) =>
|
||||||
function TargetComponent({id, ...props}) {
|
function TargetComponent({id, ...props}) {
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
navbar: {hideOnScroll},
|
||||||
themeConfig: {navbar: {hideOnScroll = false} = {}} = {},
|
} = useThemeConfig();
|
||||||
} = {},
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return <Tag {...props} />;
|
return <Tag {...props} />;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
import SearchBar from '@theme/SearchBar';
|
import SearchBar from '@theme/SearchBar';
|
||||||
import Toggle from '@theme/Toggle';
|
import Toggle from '@theme/Toggle';
|
||||||
import useThemeContext from '@theme/hooks/useThemeContext';
|
import useThemeContext from '@theme/hooks/useThemeContext';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import useHideableNavbar from '@theme/hooks/useHideableNavbar';
|
import useHideableNavbar from '@theme/hooks/useHideableNavbar';
|
||||||
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
||||||
import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';
|
import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';
|
||||||
|
@ -40,20 +41,13 @@ function splitNavItemsByPosition(items) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Navbar(): JSX.Element {
|
function Navbar(): JSX.Element {
|
||||||
|
const {isClient} = useDocusaurusContext();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
navbar: {title, items, hideOnScroll, style},
|
||||||
themeConfig: {
|
colorMode: {disableSwitch: disableColorModeSwitch},
|
||||||
navbar: {
|
} = useThemeConfig();
|
||||||
title = '',
|
|
||||||
items = [],
|
|
||||||
hideOnScroll = false,
|
|
||||||
style = undefined,
|
|
||||||
} = {},
|
|
||||||
colorMode: {disableSwitch: disableColorModeSwitch = false} = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isClient,
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
const [sidebarShown, setSidebarShown] = useState(false);
|
const [sidebarShown, setSidebarShown] = useState(false);
|
||||||
const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false);
|
const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React, {ComponentProps} from 'react';
|
import React, {ComponentProps} from 'react';
|
||||||
import Toggle from 'react-toggle';
|
import Toggle from 'react-toggle';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
@ -26,20 +26,11 @@ const Light = ({icon, style}) => (
|
||||||
|
|
||||||
export default function (props: ComponentProps<typeof Toggle>): JSX.Element {
|
export default function (props: ComponentProps<typeof Toggle>): JSX.Element {
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
colorMode: {
|
||||||
themeConfig: {
|
switchConfig: {darkIcon, darkIconStyle, lightIcon, lightIconStyle},
|
||||||
colorMode: {
|
},
|
||||||
switchConfig: {
|
} = useThemeConfig();
|
||||||
darkIcon,
|
const {isClient} = useDocusaurusContext();
|
||||||
darkIconStyle,
|
|
||||||
lightIcon,
|
|
||||||
lightIconStyle,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isClient
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Toggle
|
<Toggle
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useState, useEffect, useCallback} from 'react';
|
import {useState, useEffect, useCallback} from 'react';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
import type {useAnnouncementBarReturns} from '@theme/hooks/useAnnoucementBar';
|
import type {useAnnouncementBarReturns} from '@theme/hooks/useAnnoucementBar';
|
||||||
|
|
||||||
const STORAGE_DISMISS_KEY = 'docusaurus.announcement.dismiss';
|
const STORAGE_DISMISS_KEY = 'docusaurus.announcement.dismiss';
|
||||||
const STORAGE_ID_KEY = 'docusaurus.announcement.id';
|
const STORAGE_ID_KEY = 'docusaurus.announcement.id';
|
||||||
|
|
||||||
const useAnnouncementBar = (): useAnnouncementBarReturns => {
|
const useAnnouncementBar = (): useAnnouncementBarReturns => {
|
||||||
const {announcementBar} = useDocusaurusContext().siteConfig.themeConfig;
|
const {announcementBar} = useThemeConfig();
|
||||||
|
|
||||||
const [isClosed, setClosed] = useState(true);
|
const [isClosed, setClosed] = useState(true);
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,16 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import useThemeContext from '@theme/hooks/useThemeContext';
|
import useThemeContext from '@theme/hooks/useThemeContext';
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
import isInternalUrl from '@docusaurus/isInternalUrl';
|
import isInternalUrl from '@docusaurus/isInternalUrl';
|
||||||
import type {LogoLinkProps, useLogoReturns} from '@theme/hooks/useLogo';
|
import type {LogoLinkProps, useLogoReturns} from '@theme/hooks/useLogo';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
|
|
||||||
const useLogo = (): useLogoReturns => {
|
const useLogo = (): useLogoReturns => {
|
||||||
const {
|
const {
|
||||||
siteConfig: {themeConfig: {navbar: {logo = {}} = {}} = {}} = {},
|
navbar: {logo},
|
||||||
} = useDocusaurusContext();
|
} = useThemeConfig();
|
||||||
const {isDarkTheme} = useThemeContext();
|
const {isDarkTheme} = useThemeContext();
|
||||||
const logoLink = useBaseUrl(logo.href || '/');
|
const logoLink = useBaseUrl(logo.href || '/');
|
||||||
let logoLinkProps: LogoLinkProps = {};
|
let logoLinkProps: LogoLinkProps = {};
|
||||||
|
|
|
@ -6,15 +6,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import defaultTheme from 'prism-react-renderer/themes/palenight';
|
import defaultTheme from 'prism-react-renderer/themes/palenight';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import useThemeContext from '@theme/hooks/useThemeContext';
|
import useThemeContext from '@theme/hooks/useThemeContext';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
|
|
||||||
const usePrismTheme = (): typeof defaultTheme => {
|
const usePrismTheme = (): typeof defaultTheme => {
|
||||||
const {
|
const {prism} = useThemeConfig();
|
||||||
siteConfig: {
|
|
||||||
themeConfig: {prism = {}},
|
|
||||||
},
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
const {isDarkTheme} = useThemeContext();
|
const {isDarkTheme} = useThemeContext();
|
||||||
const lightModeTheme = prism.theme || defaultTheme;
|
const lightModeTheme = prism.theme || defaultTheme;
|
||||||
const darkModeTheme = prism.darkTheme || lightModeTheme;
|
const darkModeTheme = prism.darkTheme || lightModeTheme;
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
import {useState, useCallback, useEffect} from 'react';
|
import {useState, useCallback, useEffect} from 'react';
|
||||||
|
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||||
import type {useThemeReturns} from '@theme/hooks/useTheme';
|
import type {useThemeReturns} from '@theme/hooks/useTheme';
|
||||||
|
import useThemeConfig from '../../utils/useThemeConfig';
|
||||||
|
|
||||||
const themes = {
|
const themes = {
|
||||||
light: 'light',
|
light: 'light',
|
||||||
|
@ -38,10 +38,8 @@ const storeTheme = (newTheme) => {
|
||||||
|
|
||||||
const useTheme = (): useThemeReturns => {
|
const useTheme = (): useThemeReturns => {
|
||||||
const {
|
const {
|
||||||
siteConfig: {
|
colorMode: {disableSwitch = false},
|
||||||
themeConfig: {colorMode: {disableSwitch = false} = {}} = {},
|
} = useThemeConfig();
|
||||||
} = {},
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
const [theme, setTheme] = useState(getInitialTheme);
|
const [theme, setTheme] = useState(getInitialTheme);
|
||||||
|
|
||||||
const setLightTheme = useCallback(() => {
|
const setLightTheme = useCallback(() => {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
|
||||||
|
type ThemeConfig = {
|
||||||
|
// TODO we should complete this theme config type over time
|
||||||
|
// and share it across all themes
|
||||||
|
// and use it in the Joi validation schema?
|
||||||
|
|
||||||
|
// TODO temporary types
|
||||||
|
navbar: any;
|
||||||
|
colorMode: any;
|
||||||
|
announcementBar: any;
|
||||||
|
prism: any;
|
||||||
|
footer: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useThemeConfig(): ThemeConfig {
|
||||||
|
return useDocusaurusContext().siteConfig.themeConfig as ThemeConfig;
|
||||||
|
}
|
|
@ -23,6 +23,13 @@ const DEFAULT_COLOR_MODE_CONFIG = {
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
colorMode: DEFAULT_COLOR_MODE_CONFIG,
|
||||||
metadatas: [],
|
metadatas: [],
|
||||||
|
prism: {
|
||||||
|
additionalLanguages: [],
|
||||||
|
},
|
||||||
|
navbar: {
|
||||||
|
hideOnScroll: false,
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
@ -195,13 +202,15 @@ const ThemeConfigSchema = Joi.object({
|
||||||
}).optional(),
|
}).optional(),
|
||||||
navbar: Joi.object({
|
navbar: Joi.object({
|
||||||
style: Joi.string().equal('dark', 'primary'),
|
style: Joi.string().equal('dark', 'primary'),
|
||||||
hideOnScroll: Joi.bool().default(false),
|
hideOnScroll: Joi.bool().default(DEFAULT_CONFIG.navbar.hideOnScroll),
|
||||||
// TODO temporary (@alpha-58)
|
// TODO temporary (@alpha-58)
|
||||||
links: Joi.any().forbidden().messages({
|
links: Joi.any().forbidden().messages({
|
||||||
'any.unknown':
|
'any.unknown':
|
||||||
'themeConfig.navbar.links has been renamed as themeConfig.navbar.items',
|
'themeConfig.navbar.links has been renamed as themeConfig.navbar.items',
|
||||||
}),
|
}),
|
||||||
items: Joi.array().items(NavbarItemSchema),
|
items: Joi.array()
|
||||||
|
.items(NavbarItemSchema)
|
||||||
|
.default(DEFAULT_CONFIG.navbar.items),
|
||||||
title: Joi.string().allow('', null),
|
title: Joi.string().allow('', null),
|
||||||
logo: Joi.object({
|
logo: Joi.object({
|
||||||
alt: Joi.string().allow(''),
|
alt: Joi.string().allow(''),
|
||||||
|
@ -210,7 +219,7 @@ const ThemeConfigSchema = Joi.object({
|
||||||
href: Joi.string(),
|
href: Joi.string(),
|
||||||
target: Joi.string(),
|
target: Joi.string(),
|
||||||
}),
|
}),
|
||||||
}),
|
}).default(DEFAULT_CONFIG.navbar),
|
||||||
footer: Joi.object({
|
footer: Joi.object({
|
||||||
style: Joi.string().equal('dark', 'light').default('light'),
|
style: Joi.string().equal('dark', 'light').default('light'),
|
||||||
logo: Joi.object({
|
logo: Joi.object({
|
||||||
|
@ -219,13 +228,15 @@ const ThemeConfigSchema = Joi.object({
|
||||||
href: Joi.string(),
|
href: Joi.string(),
|
||||||
}),
|
}),
|
||||||
copyright: Joi.string(),
|
copyright: Joi.string(),
|
||||||
links: Joi.array().items(
|
links: Joi.array()
|
||||||
Joi.object({
|
.items(
|
||||||
title: Joi.string().required(),
|
Joi.object({
|
||||||
items: Joi.array().items(FooterLinkItemSchema).default([]),
|
title: Joi.string().required(),
|
||||||
}),
|
items: Joi.array().items(FooterLinkItemSchema).default([]),
|
||||||
),
|
}),
|
||||||
}),
|
)
|
||||||
|
.default([]),
|
||||||
|
}).optional(),
|
||||||
prism: Joi.object({
|
prism: Joi.object({
|
||||||
theme: Joi.object({
|
theme: Joi.object({
|
||||||
plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
|
plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
|
||||||
|
@ -236,8 +247,12 @@ const ThemeConfigSchema = Joi.object({
|
||||||
styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
|
styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
|
||||||
}),
|
}),
|
||||||
defaultLanguage: Joi.string(),
|
defaultLanguage: Joi.string(),
|
||||||
additionalLanguages: Joi.array().items(Joi.string()),
|
additionalLanguages: Joi.array()
|
||||||
}).unknown(),
|
.items(Joi.string())
|
||||||
|
.default(DEFAULT_CONFIG.prism.additionalLanguages),
|
||||||
|
})
|
||||||
|
.default(DEFAULT_CONFIG.prism)
|
||||||
|
.unknown(),
|
||||||
});
|
});
|
||||||
exports.ThemeConfigSchema = ThemeConfigSchema;
|
exports.ThemeConfigSchema = ThemeConfigSchema;
|
||||||
|
|
||||||
|
|
|
@ -24,18 +24,13 @@ import useLogo from '@theme/hooks/useLogo';
|
||||||
|
|
||||||
const Example = () => {
|
const Example = () => {
|
||||||
// highlight-next-line
|
// highlight-next-line
|
||||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={logoLink} {...logoLinkProps}>
|
<Link to={logoLink} {...logoLinkProps}>
|
||||||
{logoImageUrl != null && (
|
{logoImageUrl != null && <img src={logoImageUrl} alt={logoAlt} />}
|
||||||
<img
|
|
||||||
src={logoImageUrl}
|
|
||||||
alt={logoAlt}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
</Link>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -107,7 +102,6 @@ React Router should automatically apply active link styling to links, but you ca
|
||||||
|
|
||||||
Outbound (external) links automatically get `target="_blank" rel="noopener noreferrer"` attributes.
|
Outbound (external) links automatically get `target="_blank" rel="noopener noreferrer"` attributes.
|
||||||
|
|
||||||
|
|
||||||
## Footer
|
## Footer
|
||||||
|
|
||||||
You can add logo and a copyright to the footer via `themeConfig.footer`. Logo can be placed in [static folder](static-assets.md). Logo URL works in the same way of the navbar logo.
|
You can add logo and a copyright to the footer via `themeConfig.footer`. Logo can be placed in [static folder](static-assets.md). Logo URL works in the same way of the navbar logo.
|
||||||
|
@ -125,58 +119,58 @@ You can add logo and a copyright to the footer via `themeConfig.footer`. Logo ca
|
||||||
```
|
```
|
||||||
|
|
||||||
## Footer Links
|
## Footer Links
|
||||||
You can add links to the navbar via `themeConfig.footer.links`:
|
|
||||||
|
|
||||||
|
You can add links to the navbar via `themeConfig.footer.links`:
|
||||||
|
|
||||||
```js {5-15} title="docusaurus.config.js"
|
```js {5-15} title="docusaurus.config.js"
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// ...
|
// ...
|
||||||
footer: {
|
footer: {
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
// Label of the section of these links
|
// Label of the section of these links
|
||||||
title: 'Docs',
|
title: 'Docs',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
// Label of the link
|
// Label of the link
|
||||||
label: 'Style Guide',
|
label: 'Style Guide',
|
||||||
// Client-side routing, used for navigating within the website.
|
// Client-side routing, used for navigating within the website.
|
||||||
// The baseUrl will be automatically prepended to this value.
|
// The baseUrl will be automatically prepended to this value.
|
||||||
to: 'docs/',
|
to: 'docs/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Second Doc',
|
label: 'Second Doc',
|
||||||
to: 'docs/doc2/',
|
to: 'docs/doc2/',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Community',
|
title: 'Community',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Stack Overflow',
|
label: 'Stack Overflow',
|
||||||
// A full-page navigation, used for navigating outside of the website.
|
// A full-page navigation, used for navigating outside of the website.
|
||||||
href: 'https://stackoverflow.com/questions/tagged/docusaurus',
|
href: 'https://stackoverflow.com/questions/tagged/docusaurus',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
href: 'https://discordapp.com/invite/docusaurus',
|
href: 'https://discordapp.com/invite/docusaurus',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Twitter',
|
label: 'Twitter',
|
||||||
href: 'https://twitter.com/docusaurus',
|
href: 'https://twitter.com/docusaurus',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
//Renders the html pass-through instead of a simple link
|
//Renders the html pass-through instead of a simple link
|
||||||
html: `
|
html: `
|
||||||
<a href="https://www.netlify.com" target="_blank" rel="noreferrer noopener" aria-label="Deploys by Netlify">
|
<a href="https://www.netlify.com" target="_blank" rel="noreferrer noopener" aria-label="Deploys by Netlify">
|
||||||
<img src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" alt="Deploys by Netlify" />
|
<img src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" alt="Deploys by Netlify" />
|
||||||
</a>
|
</a>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue