mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-03 04:07:32 +02:00
feat(theme): create more generic ThemedComponent util from ThemedImage (#8890)
This commit is contained in:
parent
14586895ae
commit
f76fc1bfac
4 changed files with 105 additions and 31 deletions
|
@ -6,44 +6,21 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||
import {useColorMode} from '@docusaurus/theme-common';
|
||||
import {ThemedComponent} from '@docusaurus/theme-common';
|
||||
import type {Props} from '@theme/ThemedImage';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function ThemedImage(props: Props): JSX.Element {
|
||||
const isBrowser = useIsBrowser();
|
||||
const {colorMode} = useColorMode();
|
||||
const {sources, className, alt, ...propsRest} = props;
|
||||
|
||||
type SourceName = keyof Props['sources'];
|
||||
|
||||
const clientThemes: SourceName[] =
|
||||
colorMode === 'dark' ? ['dark'] : ['light'];
|
||||
|
||||
const renderedSourceNames: SourceName[] = isBrowser
|
||||
? clientThemes
|
||||
: // We need to render both images on the server to avoid flash
|
||||
// See https://github.com/facebook/docusaurus/pull/3730
|
||||
['light', 'dark'];
|
||||
|
||||
const {sources, className: parentClassName, alt, ...propsRest} = props;
|
||||
return (
|
||||
<>
|
||||
{renderedSourceNames.map((sourceName) => (
|
||||
<ThemedComponent className={parentClassName}>
|
||||
{({theme, className}) => (
|
||||
<img
|
||||
key={sourceName}
|
||||
src={sources[sourceName]}
|
||||
src={sources[theme]}
|
||||
alt={alt}
|
||||
className={clsx(
|
||||
styles.themedImage,
|
||||
styles[`themedImage--${sourceName}`],
|
||||
className,
|
||||
)}
|
||||
className={className}
|
||||
{...propsRest}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ThemedComponent>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* 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 React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||
import {useColorMode} from '../../contexts/colorMode';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const AllThemes = ['light', 'dark'] as const;
|
||||
|
||||
type Theme = (typeof AllThemes)[number];
|
||||
|
||||
type RenderFn = ({
|
||||
theme,
|
||||
className,
|
||||
}: {
|
||||
theme: Theme;
|
||||
className: string;
|
||||
}) => React.ReactNode;
|
||||
|
||||
type Props = {
|
||||
children: RenderFn;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic component to render anything themed in light/dark
|
||||
* Note: it's preferable to use CSS for theming because this component
|
||||
* will need to render all the variants during SSR to avoid a theme flash.
|
||||
*
|
||||
* Use this only when CSS customizations are not convenient or impossible.
|
||||
* For example, rendering themed images or SVGs...
|
||||
*
|
||||
* @param className applied to all the variants
|
||||
* @param children function to render a theme variant
|
||||
* @constructor
|
||||
*/
|
||||
export default function ThemedComponent({
|
||||
className,
|
||||
children,
|
||||
}: Props): JSX.Element {
|
||||
const isBrowser = useIsBrowser();
|
||||
const {colorMode} = useColorMode();
|
||||
|
||||
function getThemesToRender(): Theme[] {
|
||||
if (isBrowser) {
|
||||
return colorMode === 'dark' ? ['dark'] : ['light'];
|
||||
}
|
||||
// We need to render both components on the server / hydration to avoid:
|
||||
// - a flash of wrong theme before hydration
|
||||
// - React hydration mismatches
|
||||
// See https://github.com/facebook/docusaurus/pull/3730
|
||||
return ['light', 'dark'];
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{getThemesToRender().map((theme) => {
|
||||
const themedElement = children({
|
||||
theme,
|
||||
className: clsx(
|
||||
className,
|
||||
styles.themedComponent,
|
||||
styles[`themedComponent--${theme}`],
|
||||
),
|
||||
});
|
||||
return <React.Fragment key={theme}>{themedElement}</React.Fragment>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.themedComponent {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme='light'] .themedComponent--light {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .themedComponent--dark {
|
||||
display: initial;
|
||||
}
|
|
@ -24,6 +24,8 @@ export {
|
|||
type ColorModeConfig,
|
||||
} from './utils/useThemeConfig';
|
||||
|
||||
export {default as ThemedComponent} from './components/ThemedComponent';
|
||||
|
||||
export {
|
||||
createStorageSlot,
|
||||
useStorageSlot,
|
||||
|
|
Loading…
Add table
Reference in a new issue