From f2409d7f52b7c4a974028aabd7e9d5583fbc80a1 Mon Sep 17 00:00:00 2001 From: Prisma Date: Mon, 1 Apr 2024 21:13:15 +0100 Subject: [PATCH] update width of code blocks and admonition blocks --- src/css/admonition.css | 6 ++ src/theme/CodeBlock/Container/index.tsx | 21 ++++ .../CodeBlock/Container/styles.module.scss | 27 +++++ src/theme/CodeBlock/Content/Element.tsx | 17 +++ src/theme/CodeBlock/Content/String.tsx | 101 ++++++++++++++++++ src/theme/CodeBlock/Content/styles.module.css | 80 ++++++++++++++ src/theme/CodeBlock/CopyButton/index.js | 53 +++++++++ .../CodeBlock/CopyButton/styles.module.css | 40 +++++++ src/theme/CodeBlock/Line/index.js | 34 ++++++ src/theme/CodeBlock/Line/styles.module.css | 45 ++++++++ src/theme/CodeBlock/WordWrapButton/index.js | 27 +++++ .../WordWrapButton/styles.module.css | 8 ++ src/theme/CodeBlock/index.tsx | 33 ++++++ 13 files changed, 492 insertions(+) create mode 100644 src/theme/CodeBlock/Container/index.tsx create mode 100644 src/theme/CodeBlock/Container/styles.module.scss create mode 100644 src/theme/CodeBlock/Content/Element.tsx create mode 100644 src/theme/CodeBlock/Content/String.tsx create mode 100644 src/theme/CodeBlock/Content/styles.module.css create mode 100644 src/theme/CodeBlock/CopyButton/index.js create mode 100644 src/theme/CodeBlock/CopyButton/styles.module.css create mode 100644 src/theme/CodeBlock/Line/index.js create mode 100644 src/theme/CodeBlock/Line/styles.module.css create mode 100644 src/theme/CodeBlock/WordWrapButton/index.js create mode 100644 src/theme/CodeBlock/WordWrapButton/styles.module.css create mode 100644 src/theme/CodeBlock/index.tsx diff --git a/src/css/admonition.css b/src/css/admonition.css index 2bcab01d58..9d624ab581 100644 --- a/src/css/admonition.css +++ b/src/css/admonition.css @@ -61,6 +61,12 @@ [data-theme='dark'] { --if-alert-color: white; } + +.alert { + width: max-content; + max-width: 100%; +} + [data-theme='dark'] .alert { --ifm-link-hover-color: #5196d7; } diff --git a/src/theme/CodeBlock/Container/index.tsx b/src/theme/CodeBlock/Container/index.tsx new file mode 100644 index 0000000000..682d928a3e --- /dev/null +++ b/src/theme/CodeBlock/Container/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import clsx from 'clsx'; +import {ThemeClassNames, usePrismTheme} from '@docusaurus/theme-common'; +import {getPrismCssVariables} from '@docusaurus/theme-common/internal'; +import styles from './styles.module.scss'; +export default function CodeBlockContainer({as: As, ...props}) { + const prismTheme = usePrismTheme(); + const prismCssVariables = getPrismCssVariables(prismTheme); + return ( + + ); +} diff --git a/src/theme/CodeBlock/Container/styles.module.scss b/src/theme/CodeBlock/Container/styles.module.scss new file mode 100644 index 0000000000..610857f7da --- /dev/null +++ b/src/theme/CodeBlock/Container/styles.module.scss @@ -0,0 +1,27 @@ +.codeBlockContainer { + background: var(--prism-background-color); + color: var(--prism-color); + margin-bottom: var(--ifm-leading); + box-shadow: var(--ifm-global-shadow-lw); + border-radius: var(--ifm-code-border-radius); + width: max-content; + max-width: 100%; + + ::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + background-color: transparent; + } + ::-webkit-scrollbar-thumb { + border-radius: 10px; + background-color: #c5c6c8; + } + ::-webkit-scrollbar-track { + border-radius: 10px; + background-color: transparent; + } + ::-webkit-scrollbar-corner { + background-color: transparent; + border-color: transparent; + } +} diff --git a/src/theme/CodeBlock/Content/Element.tsx b/src/theme/CodeBlock/Content/Element.tsx new file mode 100644 index 0000000000..84ffd0a993 --- /dev/null +++ b/src/theme/CodeBlock/Content/Element.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import Container from '../Container'; +//
 tags in markdown map to CodeBlocks. They may contain JSX children. When
+// the children is not a simple string, we just return a styled block without
+// actually highlighting.
+export default function CodeBlockJSX({children, className}) {
+  return (
+    
+      {children}
+    
+  );
+}
diff --git a/src/theme/CodeBlock/Content/String.tsx b/src/theme/CodeBlock/Content/String.tsx
new file mode 100644
index 0000000000..dcf66bbc20
--- /dev/null
+++ b/src/theme/CodeBlock/Content/String.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import clsx from 'clsx';
+import {useThemeConfig, usePrismTheme} from '@docusaurus/theme-common';
+import {
+  parseCodeBlockTitle,
+  parseLanguage,
+  parseLines,
+  containsLineNumbers,
+  useCodeWordWrap,
+} from '@docusaurus/theme-common/internal';
+import {Highlight} from 'prism-react-renderer';
+import styles from './styles.module.css';
+import Container from '../Container';
+import WordWrapButton from '../WordWrapButton';
+import CopyButton from '../CopyButton';
+import Line from '../Line';
+// Prism languages are always lowercase
+// We want to fail-safe and allow both "php" and "PHP"
+// See https://github.com/facebook/docusaurus/issues/9012
+function normalizeLanguage(language) {
+  return language?.toLowerCase();
+}
+export default function CodeBlockString({
+  children,
+  className: blockClassName = '',
+  metastring,
+  title: titleProp,
+  showLineNumbers: showLineNumbersProp,
+  language: languageProp,
+}) {
+  const {
+    prism: {defaultLanguage, magicComments},
+  } = useThemeConfig();
+  const language = normalizeLanguage(
+    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:
+  // "title=\"xyz\"" => title: "\"xyz\""
+  const title = parseCodeBlockTitle(metastring) || titleProp;
+  const {lineClassNames, code} = parseLines(children, {
+    metastring,
+    language,
+    magicComments,
+  });
+  const showLineNumbers =
+    showLineNumbersProp ?? containsLineNumbers(metastring);
+  return (
+    
+      {title && 
{title}
} +
+ + {({className, style, tokens, getLineProps, getTokenProps}) => ( +
+              
+                {tokens.map((line, i) => (
+                  
+                ))}
+              
+            
+ )} +
+
+ {(wordWrap.isEnabled || wordWrap.isCodeScrollable) && ( + wordWrap.toggle()} + isEnabled={wordWrap.isEnabled} + /> + )} + +
+
+
+ ); +} diff --git a/src/theme/CodeBlock/Content/styles.module.css b/src/theme/CodeBlock/Content/styles.module.css new file mode 100644 index 0000000000..31eda1e028 --- /dev/null +++ b/src/theme/CodeBlock/Content/styles.module.css @@ -0,0 +1,80 @@ +.codeBlockContent { + position: relative; + /* rtl:ignore */ + direction: ltr; + border-radius: inherit; +} + +.codeBlockTitle { + border-bottom: 1px solid var(--ifm-color-emphasis-300); + font-size: var(--ifm-code-font-size); + font-weight: 500; + padding: 0.75rem var(--ifm-pre-padding); + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} + +.codeBlock { + --ifm-pre-background: var(--prism-background-color); + margin: 0; + padding-bottom: 0 !important; +} + +.codeBlockTitle + .codeBlockContent .codeBlock { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.codeBlockStandalone { + padding: 0; +} + +.codeBlockLines { + font: inherit; + /* rtl:ignore */ + float: left; + min-width: 100%; + padding: var(--ifm-pre-padding); +} + +.codeBlockLinesWithNumbering { + display: table; + padding: var(--ifm-pre-padding) 0; +} + +@media print { + .codeBlockLines { + white-space: pre-wrap; + } +} + +.buttonGroup { + display: flex; + column-gap: 0.2rem; + position: absolute; + /* rtl:ignore */ + 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 var(--ifm-transition-fast) 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; +} diff --git a/src/theme/CodeBlock/CopyButton/index.js b/src/theme/CodeBlock/CopyButton/index.js new file mode 100644 index 0000000000..fd430b473c --- /dev/null +++ b/src/theme/CodeBlock/CopyButton/index.js @@ -0,0 +1,53 @@ +import React, {useCallback, useState, useRef, useEffect} from 'react'; +import clsx from 'clsx'; +import copy from 'copy-text-to-clipboard'; +import {translate} from '@docusaurus/Translate'; +import IconCopy from '@theme/Icon/Copy'; +import IconSuccess from '@theme/Icon/Success'; +import styles from './styles.module.css'; +export default function CopyButton({code, className}) { + const [isCopied, setIsCopied] = useState(false); + const copyTimeout = useRef(undefined); + const handleCopyCode = useCallback(() => { + copy(code); + setIsCopied(true); + copyTimeout.current = window.setTimeout(() => { + setIsCopied(false); + }, 1000); + }, [code]); + useEffect(() => () => window.clearTimeout(copyTimeout.current), []); + return ( + + ); +} diff --git a/src/theme/CodeBlock/CopyButton/styles.module.css b/src/theme/CodeBlock/CopyButton/styles.module.css new file mode 100644 index 0000000000..d5268e900b --- /dev/null +++ b/src/theme/CodeBlock/CopyButton/styles.module.css @@ -0,0 +1,40 @@ +:global(.theme-code-block:hover) .copyButtonCopied { + opacity: 1 !important; +} + +.copyButtonIcons { + position: relative; + width: 1.125rem; + height: 1.125rem; +} + +.copyButtonIcon, +.copyButtonSuccessIcon { + position: absolute; + top: 0; + left: 0; + fill: currentColor; + opacity: inherit; + width: inherit; + height: inherit; + transition: all var(--ifm-transition-fast) ease; +} + +.copyButtonSuccessIcon { + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.33); + opacity: 0; + color: #00d600; +} + +.copyButtonCopied .copyButtonIcon { + transform: scale(0.33); + opacity: 0; +} + +.copyButtonCopied .copyButtonSuccessIcon { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + transition-delay: 0.075s; +} diff --git a/src/theme/CodeBlock/Line/index.js b/src/theme/CodeBlock/Line/index.js new file mode 100644 index 0000000000..f36761806a --- /dev/null +++ b/src/theme/CodeBlock/Line/index.js @@ -0,0 +1,34 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +export default function CodeBlockLine({ + line, + classNames, + showLineNumbers, + getLineProps, + getTokenProps, +}) { + if (line.length === 1 && line[0].content === '\n') { + line[0].content = ''; + } + const lineProps = getLineProps({ + line, + className: clsx(classNames, showLineNumbers && styles.codeLine), + }); + const lineTokens = line.map((token, key) => ( + + )); + return ( + + {showLineNumbers ? ( + <> + + {lineTokens} + + ) : ( + lineTokens + )} +
+
+ ); +} diff --git a/src/theme/CodeBlock/Line/styles.module.css b/src/theme/CodeBlock/Line/styles.module.css new file mode 100644 index 0000000000..7c28ed9aa3 --- /dev/null +++ b/src/theme/CodeBlock/Line/styles.module.css @@ -0,0 +1,45 @@ +/* Intentionally has zero specificity, so that to be able to override +the background in custom CSS file due bug https://github.com/facebook/docusaurus/issues/3678 */ +:where(:root) { + --docusaurus-highlighted-code-line-bg: rgb(72 77 91); +} + +:where([data-theme='dark']) { + --docusaurus-highlighted-code-line-bg: rgb(100 100 100); +} + +:global(.theme-code-block-highlighted-line) { + background-color: var(--docusaurus-highlighted-code-line-bg); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +.codeLine { + display: table-row; + counter-increment: line-count; +} + +.codeLineNumber { + display: table-cell; + text-align: right; + width: 1%; + position: sticky; + left: 0; + padding: 0 var(--ifm-pre-padding); + background: var(--ifm-pre-background); + overflow-wrap: normal; +} + +.codeLineNumber::before { + content: counter(line-count); + opacity: 0.4; +} + +:global(.theme-code-block-highlighted-line) .codeLineNumber::before { + opacity: 0.8; +} + +.codeLineContent { + padding-right: var(--ifm-pre-padding); +} diff --git a/src/theme/CodeBlock/WordWrapButton/index.js b/src/theme/CodeBlock/WordWrapButton/index.js new file mode 100644 index 0000000000..3a4136b630 --- /dev/null +++ b/src/theme/CodeBlock/WordWrapButton/index.js @@ -0,0 +1,27 @@ +import React from 'react'; +import clsx from 'clsx'; +import {translate} from '@docusaurus/Translate'; +import IconWordWrap from '@theme/Icon/WordWrap'; +import styles from './styles.module.css'; +export default function WordWrapButton({className, onClick, isEnabled}) { + 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 ( + + ); +} diff --git a/src/theme/CodeBlock/WordWrapButton/styles.module.css b/src/theme/CodeBlock/WordWrapButton/styles.module.css new file mode 100644 index 0000000000..fdbc894646 --- /dev/null +++ b/src/theme/CodeBlock/WordWrapButton/styles.module.css @@ -0,0 +1,8 @@ +.wordWrapButtonIcon { + width: 1.2rem; + height: 1.2rem; +} + +.wordWrapButtonEnabled .wordWrapButtonIcon { + color: var(--ifm-color-primary); +} diff --git a/src/theme/CodeBlock/index.tsx b/src/theme/CodeBlock/index.tsx new file mode 100644 index 0000000000..dc5e34ecd9 --- /dev/null +++ b/src/theme/CodeBlock/index.tsx @@ -0,0 +1,33 @@ +import React, {isValidElement} from 'react'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import CodeBlockString from './Content/String'; +import CodeBlockJSX from './Content/Element'; +/** + * Best attempt to make the children a plain string so it is copyable. If there + * are react elements, we will not be able to copy the content, and it will + * return `children` as-is; otherwise, it concatenates the string children + * together. + */ +function maybeStringifyChildren(children) { + if (React.Children.toArray(children).some((el) => isValidElement(el))) { + return children; + } + // The children is now guaranteed to be one/more plain strings + return Array.isArray(children) ? children.join('') : children; +} +export default function CodeBlock({children: rawChildren, ...props}) { + // The Prism theme on SSR is always the default theme but the site theme can + // be in a different mode. React hydration doesn't update DOM styles that come + // from SSR. Hence force a re-render after mounting to apply the current + // relevant styles. + const isBrowser = useIsBrowser(); + const children = maybeStringifyChildren(rawChildren); + const CodeBlockComp = + typeof children === 'string' ? CodeBlockString : CodeBlockJSX; + return ( + //@ts-ignore + + {children} + + ); +}