diff --git a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts index 4d0b08fa94..a1d095387e 100644 --- a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts +++ b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts @@ -104,6 +104,7 @@ export type TableOfContents = { maxHeadingLevel: number; }; +// TODO Docusaurus v4: use interface + declaration merging to enhance // Theme config after validation/normalization export type ThemeConfig = { docs: { diff --git a/packages/docusaurus-theme-live-codeblock/src/theme-live-codeblock.d.ts b/packages/docusaurus-theme-live-codeblock/src/theme-live-codeblock.d.ts index 1047302e70..6b2909123f 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme-live-codeblock.d.ts +++ b/packages/docusaurus-theme-live-codeblock/src/theme-live-codeblock.d.ts @@ -39,6 +39,33 @@ declare module '@theme/Playground' { export default function Playground(props: LiveProviderProps): ReactNode; } +declare module '@theme/Playground/Preview' { + import type {ReactNode} from 'react'; + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface Props {} + + export default function PlaygroundPreview(props: Props): ReactNode; +} + +declare module '@theme/Playground/Editor' { + import type {ReactNode} from 'react'; + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface Props {} + + export default function PlaygroundEditor(props: Props): ReactNode; +} + +declare module '@theme/Playground/Header' { + import type {ReactNode} from 'react'; + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface Props {} + + export default function PlaygroundHeader(props: Props): ReactNode; +} + declare module '@theme/ReactLiveScope' { type Scope = { [key: string]: unknown; diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/index.tsx new file mode 100644 index 0000000000..3ba90571cb --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/index.tsx @@ -0,0 +1,23 @@ +/** + * 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, {type ReactNode} from 'react'; +import {LiveEditor} from 'react-live'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import styles from './styles.module.css'; + +export default function PlaygroundEditor(): ReactNode { + const isBrowser = useIsBrowser(); + return ( + + ); +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/styles.module.css new file mode 100644 index 0000000000..3b5165baf9 --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Editor/styles.module.css @@ -0,0 +1,17 @@ +/** + * 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. + */ + +.playgroundEditor { + font: var(--ifm-code-font-size) / var(--ifm-pre-line-height) + var(--ifm-font-family-monospace) !important; + /* rtl:ignore */ + direction: ltr; +} + +.playgroundEditor pre { + border-radius: 0; +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/index.tsx new file mode 100644 index 0000000000..5d1e4d2499 --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/index.tsx @@ -0,0 +1,19 @@ +/** + * 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, {type ReactNode} from 'react'; +import clsx from 'clsx'; + +import styles from './styles.module.css'; + +export default function PlaygroundHeader({ + children, +}: { + children: ReactNode; +}): ReactNode { + return
{children}
; +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/styles.module.css new file mode 100644 index 0000000000..092af7825d --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Header/styles.module.css @@ -0,0 +1,21 @@ +/** + * 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. + */ + +.playgroundHeader { + letter-spacing: 0.08rem; + padding: 0.75rem; + text-transform: uppercase; + font-weight: bold; + background: var(--ifm-color-emphasis-200); + color: var(--ifm-color-content); + font-size: var(--ifm-code-font-size); +} + +.playgroundHeader:first-of-type { + background: var(--ifm-color-emphasis-600); + color: var(--ifm-color-content-inverse); +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/index.tsx new file mode 100644 index 0000000000..b4f6e1ef88 --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/index.tsx @@ -0,0 +1,59 @@ +/** + * 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, {type ReactNode} from 'react'; +import {LiveError, LivePreview} from 'react-live'; +import BrowserOnly from '@docusaurus/BrowserOnly'; +import {ErrorBoundaryErrorMessageFallback} from '@docusaurus/theme-common'; +import ErrorBoundary from '@docusaurus/ErrorBoundary'; +import Translate from '@docusaurus/Translate'; +import PlaygroundHeader from '@theme/Playground/Header'; + +import styles from './styles.module.css'; + +function Loader() { + // Is it worth improving/translating? + // eslint-disable-next-line @docusaurus/no-untranslated-text + return
Loading...
; +} + +function PlaygroundLivePreview(): ReactNode { + // No SSR for the live preview + // See https://github.com/facebook/docusaurus/issues/5747 + return ( + }> + {() => ( + <> + ( + + )}> + + + + + )} + + ); +} + +export default function PlaygroundPreview(): ReactNode { + return ( + <> + + + Result + + +
+ +
+ + ); +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/styles.module.css new file mode 100644 index 0000000000..b22c113f10 --- /dev/null +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/Preview/styles.module.css @@ -0,0 +1,11 @@ +/** + * 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. + */ + +.playgroundPreview { + padding: 1rem; + background-color: var(--ifm-pre-background); +} diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx index 37cda3ee02..1a078c549b 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.tsx @@ -6,137 +6,74 @@ */ import React, {type ReactNode} from 'react'; -import clsx from 'clsx'; -import useIsBrowser from '@docusaurus/useIsBrowser'; -import {LiveProvider, LiveEditor, LiveError, LivePreview} from 'react-live'; -import Translate from '@docusaurus/Translate'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import BrowserOnly from '@docusaurus/BrowserOnly'; -import { - ErrorBoundaryErrorMessageFallback, - usePrismTheme, -} from '@docusaurus/theme-common'; -import ErrorBoundary from '@docusaurus/ErrorBoundary'; +import {LiveProvider} from 'react-live'; +import {usePrismTheme, useThemeConfig} from '@docusaurus/theme-common'; +import PlaygroundPreview from '@theme/Playground/Preview'; +import PlaygroundEditor from '@theme/Playground/Editor'; import type {Props} from '@theme/Playground'; import type {ThemeConfig} from '@docusaurus/theme-live-codeblock'; import styles from './styles.module.css'; -function Header({children}: {children: ReactNode}) { - return
{children}
; -} - -function LivePreviewLoader() { - // Is it worth improving/translating? - // eslint-disable-next-line @docusaurus/no-untranslated-text - return
Loading...
; -} - -function Preview() { - // No SSR for the live preview - // See https://github.com/facebook/docusaurus/issues/5747 - return ( - }> - {() => ( - <> - ( - - )}> - - - - - )} - - ); -} - -function ResultWithHeader() { - return ( - <> -
- - Result - -
- {/* https://github.com/facebook/docusaurus/issues/5747 */} -
- -
- - ); -} - -function ThemedLiveEditor() { - const isBrowser = useIsBrowser(); - return ( - - ); -} - -function EditorWithHeader() { - return ( - <> -
- - Live Editor - -
- - - ); -} - // this should rather be a stable function // see https://github.com/facebook/docusaurus/issues/9630#issuecomment-1855682643 const DEFAULT_TRANSFORM_CODE = (code: string) => `${code};`; +type PlaygroundProviderProps = Omit & { + code: string | undefined; + children: ReactNode; +}; + +function PlaygroundProvider(props: PlaygroundProviderProps): ReactNode { + const prismTheme = usePrismTheme(); + const noInline = props.metastring?.includes('noInline') ?? false; + return ( + + + + ); +} + +function useLiveCodeBlockThemeConfig() { + const themeConfig = useThemeConfig() as unknown as ThemeConfig; + return themeConfig.liveCodeBlock; +} + +function PlaygroundContent(): ReactNode { + const {playgroundPosition} = useLiveCodeBlockThemeConfig(); + return ( + <> + {playgroundPosition === 'top' ? ( + <> + + + + ) : ( + <> + + + + )} + + ); +} + export default function Playground({ children, transformCode, ...props }: Props): ReactNode { - const { - siteConfig: {themeConfig}, - } = useDocusaurusContext(); - const { - liveCodeBlock: {playgroundPosition}, - } = themeConfig as ThemeConfig; - const prismTheme = usePrismTheme(); - - const noInline = props.metastring?.includes('noInline') ?? false; - return (
- - {playgroundPosition === 'top' ? ( - <> - - - - ) : ( - <> - - - - )} - + + +
); } diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css index 6696d31335..a0387c648c 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css +++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css @@ -26,19 +26,3 @@ background: var(--ifm-color-emphasis-600); color: var(--ifm-color-content-inverse); } - -.playgroundEditor { - font: var(--ifm-code-font-size) / var(--ifm-pre-line-height) - var(--ifm-font-family-monospace) !important; - /* rtl:ignore */ - direction: ltr; -} - -.playgroundEditor pre { - border-radius: 0; -} - -.playgroundPreview { - padding: 1rem; - background-color: var(--ifm-pre-background); -}