Skip to content

Commit

Permalink
feat(theme): create more generic ThemedComponent util from ThemedImage (
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber committed Apr 13, 2023
1 parent 1458689 commit f76fc1b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 31 deletions.
39 changes: 8 additions & 31 deletions packages/docusaurus-theme-classic/src/theme/ThemedImage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
);
}
Original file line number Diff line number Diff line change
@@ -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>;
})}
</>
);
}
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions packages/docusaurus-theme-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export {
type ColorModeConfig,
} from './utils/useThemeConfig';

export {default as ThemedComponent} from './components/ThemedComponent';

export {
createStorageSlot,
useStorageSlot,
Expand Down

0 comments on commit f76fc1b

Please sign in to comment.