Skip to content

Commit

Permalink
feat(theme-classic): toggle code wrap button (#7036)
Browse files Browse the repository at this point in the history
Co-authored-by: Sébastien Lorber <[email protected]>
Co-authored-by: sebastienlorber <[email protected]>
  • Loading branch information
3 people authored Apr 22, 2022
1 parent c3add31 commit 4e4aa6a
Show file tree
Hide file tree
Showing 33 changed files with 204 additions and 24 deletions.
11 changes: 11 additions & 0 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ declare module '@theme/CodeBlock' {
declare module '@theme/CodeBlock/CopyButton' {
export interface Props {
readonly code: string;
readonly className?: string;
}

export default function CopyButton(props: Props): JSX.Element;
Expand Down Expand Up @@ -220,6 +221,16 @@ declare module '@theme/CodeBlock/Line' {
export default function CodeBlockLine(props: Props): JSX.Element;
}

declare module '@theme/CodeBlock/WordWrapButton' {
export interface Props {
readonly className?: string;
readonly onClick: React.MouseEventHandler;
readonly isEnabled: boolean;
}

export default function WordWrapButton(props: Props): JSX.Element;
}

declare module '@theme/DocCard' {
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import {
parseLines,
containsLineNumbers,
usePrismTheme,
useCodeWordWrap,
} from '@docusaurus/theme-common';
import clsx from 'clsx';
import Highlight, {defaultProps, type Language} from 'prism-react-renderer';
import Line from '@theme/CodeBlock/Line';
import CopyButton from '@theme/CodeBlock/CopyButton';
import WordWrapButton from '@theme/CodeBlock/WordWrapButton';
import Container from '@theme/CodeBlock/Container';
import type {Props} from '@theme/CodeBlock/Content/String';

Expand All @@ -37,6 +39,7 @@ export default function CodeBlockString({
const language =
languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage;
const prismTheme = usePrismTheme();
const wordWrap = useCodeWordWrap();

// We still parse the metastring in case we want to support more syntax in the
// future. Note that MDX doesn't strip quotes when parsing metastring:
Expand Down Expand Up @@ -67,6 +70,7 @@ export default function CodeBlockString({
<pre
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
ref={wordWrap.codeBlockRef}
className={clsx(className, styles.codeBlock, 'thin-scrollbar')}>
<code
className={clsx(
Expand All @@ -87,7 +91,16 @@ export default function CodeBlockString({
</pre>
)}
</Highlight>
<CopyButton code={code} />
<div className={styles.buttonGroup}>
{(wordWrap.isEnabled || wordWrap.isCodeScrollable) && (
<WordWrapButton
className={styles.codeButton}
onClick={() => wordWrap.toggle()}
isEnabled={wordWrap.isEnabled}
/>
)}
<CopyButton className={styles.codeButton} code={code} />
</div>
</div>
</Container>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@
white-space: pre-wrap;
}
}

.buttonGroup {
display: flex;
column-gap: 0.2rem;
position: absolute;
right: calc(var(--ifm-pre-padding) / 2);
top: calc(var(--ifm-pre-padding) / 2);
}

.buttonGroup button {
display: flex;
align-items: center;
background: var(--prism-background-color);
color: var(--prism-color);
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
padding: 0.4rem;
line-height: 0;
transition: opacity 200ms ease-in-out;
opacity: 0;
}

.buttonGroup button:focus-visible,
.buttonGroup button:hover {
opacity: 1 !important;
}

:global(.theme-code-block:hover) .buttonGroup button {
opacity: 0.4;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {Props} from '@theme/CodeBlock/CopyButton';

import styles from './styles.module.css';

export default function CopyButton({code}: Props): JSX.Element {
export default function CopyButton({code, className}: Props): JSX.Element {
const [isCopied, setIsCopied] = useState(false);
const copyTimeout = useRef<number | undefined>(undefined);
const handleCopyCode = useCallback(() => {
Expand Down Expand Up @@ -48,8 +48,9 @@ export default function CopyButton({code}: Props): JSX.Element {
description: 'The copy button label on code blocks',
})}
className={clsx(
styles.copyButton,
'clean-btn',
className,
styles.copyButton,
isCopied && styles.copyButtonCopied,
)}
onClick={handleCopyCode}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/

.copyButton {
display: flex;
/* TODO: move to base button styling */
background: var(--prism-background-color);
color: var(--prism-color);
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
padding: 0.4rem;
position: absolute;
right: calc(var(--ifm-pre-padding) / 2);
top: calc(var(--ifm-pre-padding) / 2);
transition: opacity 200ms ease-in-out;
opacity: 0;
}

.copyButton:focus-visible,
.copyButton:hover,
:global(.theme-code-block:hover) .copyButtonCopied {
opacity: 1 !important;
}

:global(.theme-code-block:hover) .copyButton:not(.copyButtonCopied) {
opacity: 0.4;
}

.copyButtonIcons {
position: relative;
width: 1.125rem;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 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 {translate} from '@docusaurus/Translate';
import type {Props} from '@theme/CodeBlock/WordWrapButton';

import styles from './styles.module.css';

export default function WordWrapButton({
className,
onClick,
isEnabled,
}: Props): JSX.Element | null {
const title = translate({
id: 'theme.CodeBlock.wordWrapToggle',
message: 'Toggle word wrap',
description:
'The title attribute for toggle word wrapping button of code block lines',
});

return (
<button
type="button"
onClick={onClick}
className={clsx(
'clean-btn',
className,
isEnabled && styles.wordWrapButtonEnabled,
)}
aria-label={title}
title={title}>
<svg
className={styles.wordWrapButtonIcon}
viewBox="0 0 24 24"
aria-hidden="true">
<path
fill="currentColor"
d="M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"
/>
</svg>
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* 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.
*/

.wordWrapButtonIcon {
width: 1.2rem;
height: 1.2rem;
}

.wordWrapButtonEnabled .wordWrapButtonIcon {
color: var(--ifm-color-primary);
}
56 changes: 56 additions & 0 deletions packages/docusaurus-theme-common/src/hooks/useCodeWordWrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* 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 type {RefObject} from 'react';
import {useState, useCallback, useEffect, useRef} from 'react';

export function useCodeWordWrap(): {
readonly codeBlockRef: RefObject<HTMLPreElement>;
readonly isEnabled: boolean;
readonly isCodeScrollable: boolean;
readonly toggle: () => void;
} {
const [isEnabled, setIsEnabled] = useState(false);
const [isCodeScrollable, setIsCodeScrollable] = useState<boolean>(false);
const codeBlockRef = useRef<HTMLPreElement>(null);

const toggle = useCallback(() => {
const codeElement = codeBlockRef.current!.querySelector('code')!;

if (isEnabled) {
codeElement.removeAttribute('style');
} else {
codeElement.style.whiteSpace = 'pre-wrap';
}

setIsEnabled((value) => !value);
}, [codeBlockRef, isEnabled]);

const updateCodeIsScrollable = useCallback(() => {
const {scrollWidth, clientWidth} = codeBlockRef.current!;
const isScrollable =
scrollWidth > clientWidth ||
codeBlockRef.current!.querySelector('code')!.hasAttribute('style');
setIsCodeScrollable(isScrollable);
}, [codeBlockRef]);

useEffect(() => {
updateCodeIsScrollable();
}, [isEnabled, updateCodeIsScrollable]);

useEffect(() => {
window.addEventListener('resize', updateCodeIsScrollable, {
passive: true,
});

return () => {
window.removeEventListener('resize', updateCodeIsScrollable);
};
}, [updateCodeIsScrollable]);

return {codeBlockRef, isEnabled, isCodeScrollable, toggle};
}
1 change: 1 addition & 0 deletions packages/docusaurus-theme-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ export {usePrismTheme} from './hooks/usePrismTheme';
export {useLockBodyScroll} from './hooks/useLockBodyScroll';
export {useWindowSize} from './hooks/useWindowSize';
export {useSearchPage} from './hooks/useSearchPage';
export {useCodeWordWrap} from './hooks/useCodeWordWrap';
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "تم النسخ",
"theme.CodeBlock.copy": "نسخ",
"theme.CodeBlock.copyButtonAriaLabel": "نسخ الرمز إلى الحافظة",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"theme.CodeBlock.copy___DESCRIPTION": "The copy button label on code blocks",
"theme.CodeBlock.copyButtonAriaLabel": "Copy code to clipboard",
"theme.CodeBlock.copyButtonAriaLabel___DESCRIPTION": "The ARIA label for copy code blocks button",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.CodeBlock.wordWrapToggle___DESCRIPTION": "The title attribute for toggle word wrapping button of code block lines",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel___DESCRIPTION": "The ARIA label to toggle the collapsible sidebar category",
"theme.ErrorPageContent.title": "This page crashed.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "কপিড",
"theme.CodeBlock.copy": "কপি",
"theme.CodeBlock.copyButtonAriaLabel": "ক্লিপবোর্ডে কোড কপি করুন",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Zkopírováno",
"theme.CodeBlock.copy": "Zkopírovat",
"theme.CodeBlock.copyButtonAriaLabel": "Zkopírovat kód do schránky",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Kopieret",
"theme.CodeBlock.copy": "Kopier",
"theme.CodeBlock.copyButtonAriaLabel": "Kopier kode til udklipsholder",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Kopiert",
"theme.CodeBlock.copy": "Kopieren",
"theme.CodeBlock.copyButtonAriaLabel": "In die Zwischenablage kopieren",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Umschalten der Seitenleiste mit einklappbarer Kategorie '{label}'",
"theme.ErrorPageContent.title": "Die Seite ist abgestürzt.",
"theme.ErrorPageContent.tryAgain": "Nochmal versuchen",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Copiado",
"theme.CodeBlock.copy": "Copiar",
"theme.CodeBlock.copyButtonAriaLabel": "Copiar código al portapapeles",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "کپی شد",
"theme.CodeBlock.copy": "کپی",
"theme.CodeBlock.copyButtonAriaLabel": "کپی به کلیپ بورد",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Kinopya",
"theme.CodeBlock.copy": "Kopyahin",
"theme.CodeBlock.copyButtonAriaLabel": "Kopyahin ang code sa clipboard",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Copié",
"theme.CodeBlock.copy": "Copier",
"theme.CodeBlock.copyButtonAriaLabel": "Copier le code",
"theme.CodeBlock.wordWrapToggle": "Basculer le retour à la ligne",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "Cette page a planté.",
"theme.ErrorPageContent.tryAgain": "Réessayer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "הועתק",
"theme.CodeBlock.copy": "העתק",
"theme.CodeBlock.copyButtonAriaLabel": "העתק קוד ללוח העריכה",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "कॉपीड",
"theme.CodeBlock.copy": "कॉपी",
"theme.CodeBlock.copyButtonAriaLabel": "क्लिपबोर्ड पर कोड कॉपी करें",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "Copiato",
"theme.CodeBlock.copy": "Copia",
"theme.CodeBlock.copyButtonAriaLabel": "Copia il codice negli appunti",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Attiva/disattiva la categoria '{label}' della barra laterale collassabile",
"theme.ErrorPageContent.title": "Questa pagina è andata in crash.",
"theme.ErrorPageContent.tryAgain": "Prova di nuovo",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"theme.CodeBlock.copied": "コピーしました",
"theme.CodeBlock.copy": "コピー",
"theme.CodeBlock.copyButtonAriaLabel": "クリップボードにコードをコピー",
"theme.CodeBlock.wordWrapToggle": "Toggle word wrap",
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
"theme.ErrorPageContent.title": "This page crashed.",
"theme.ErrorPageContent.tryAgain": "Try again",
Expand Down
Loading

0 comments on commit 4e4aa6a

Please sign in to comment.