From 7b7d1e6161141cdb2c3d2238cdd0109aa2c325bf Mon Sep 17 00:00:00 2001 From: Bruno Vego Date: Fri, 12 Jul 2019 07:55:56 +0200 Subject: [PATCH] feat(v2): CodeBlock copy button (#1643) * feat(v2): CodeBlock copy button * fix: live theme editor breaking bug --- .../docusaurus-theme-classic/package.json | 1 + .../src/theme/CodeBlock/index.js | 62 +++++++++++++++--- .../src/theme/CodeBlock/styles.module.css | 28 +++++++++ .../package.json | 1 + .../src/theme/CodeBlock/index.js | 63 ++++++++++++++++--- .../src/theme/CodeBlock/styles.module.css | 28 +++++++++ yarn.lock | 2 +- 7 files changed, 164 insertions(+), 21 deletions(-) diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 3d3a763c90..4e6bc4bd6e 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -11,6 +11,7 @@ "@mdx-js/mdx": "^1.0.20", "@mdx-js/react": "^1.0.20", "classnames": "^2.2.6", + "clipboard": "^2.0.4", "infima": "0.2.0-alpha.2", "prism-react-renderer": "^0.1.6", "react-toggle": "^4.0.2" diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js index 97faaeb94b..2a3ac6218b 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js @@ -5,15 +5,44 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {useEffect, useState, useRef} from 'react'; import classnames from 'classnames'; import Highlight, {defaultProps} from 'prism-react-renderer'; import nightOwlTheme from 'prism-react-renderer/themes/nightOwl'; +import Clipboard from 'clipboard'; import styles from './styles.module.css'; export default ({children, className: languageClassName}) => { + const [showCopied, setShowCopied] = useState(false); + const target = useRef(null); + const button = useRef(null); + + useEffect(() => { + let clipboard; + + if (button.current) { + clipboard = new Clipboard(button.current, { + target: () => target.current, + }); + } + + return () => { + if (clipboard) { + clipboard.destroy(); + } + }; + }, [button.current, target.current]); + const language = languageClassName && languageClassName.replace(/language-/, ''); + + const handleCopyCode = () => { + window.getSelection().empty(); + setShowCopied(true); + + setTimeout(() => setShowCopied(false), 2000); + }; + return ( { code={children.trim()} language={language}> {({className, style, tokens, getLineProps, getTokenProps}) => ( -
-          {tokens.map((line, i) => (
-            
- {line.map((token, key) => ( - - ))} -
- ))} -
+
+
+            {tokens.map((line, i) => (
+              
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ +
)}
); diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css index b6d81cf1f7..b8b2a53162 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css @@ -6,3 +6,31 @@ overflow-wrap: break-word; white-space: pre-wrap; } + +.codeBlockWrapper { + position: relative; +} + +.codeBlockWrapper:hover > .copyButton { + bottom: calc(var(--ifm-pre-padding) - 2px); + visibility: visible; + opacity: 1; +} + +.copyButton { + position: absolute; + right: var(--ifm-pre-padding); + bottom: calc(var(--ifm-pre-padding) - 4px); + padding: 4px 8px; + visibility: hidden; + opacity: 0; + transition: opacity 200ms ease-in-out, visibility 200ms ease-in-out, + bottom 200ms ease-in-out; + border: 1px solid rgb(214, 222, 235); + border-radius: var(--ifm-global-radius); + outline: none; + cursor: pointer; + line-height: 12px; + background: rgb(1, 22, 39); + color: rgb(214, 222, 235); +} diff --git a/packages/docusaurus-theme-live-codeblock/package.json b/packages/docusaurus-theme-live-codeblock/package.json index 88fb3a516b..a4b6e936d3 100644 --- a/packages/docusaurus-theme-live-codeblock/package.json +++ b/packages/docusaurus-theme-live-codeblock/package.json @@ -9,6 +9,7 @@ "license": "MIT", "dependencies": { "classnames": "^2.2.6", + "clipboard": "^2.0.4", "prism-react-renderer": "^0.1.6", "react-live": "^2.1.2", "react-loadable": "^5.5.0", diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/index.js b/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/index.js index 0ee5e3e80e..88fedad743 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/index.js +++ b/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/index.js @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {useEffect, useState, useRef} from 'react'; import classnames from 'classnames'; import LoadableVisibility from 'react-loadable-visibility/react-loadable'; import Highlight, {defaultProps} from 'prism-react-renderer'; import nightOwlTheme from 'prism-react-renderer/themes/nightOwl'; +import Clipboard from 'clipboard'; import Loading from '@theme/Loading'; import styles from './styles.module.css'; @@ -20,6 +21,26 @@ const Playground = LoadableVisibility({ }); export default ({children, className: languageClassName, live, ...props}) => { + const [showCopied, setShowCopied] = useState(false); + const target = useRef(null); + const button = useRef(null); + + useEffect(() => { + let clipboard; + + if (button.current) { + clipboard = new Clipboard(button.current, { + target: () => target.current, + }); + } + + return () => { + if (clipboard) { + clipboard.destroy(); + } + }; + }, [button.current, target.current]); + if (live) { return ( { /> ); } + const language = languageClassName && languageClassName.replace(/language-/, ''); + + const handleCopyCode = () => { + window.getSelection().empty(); + setShowCopied(true); + + setTimeout(() => setShowCopied(false), 2000); + }; + return ( { code={children.trim()} language={language}> {({className, style, tokens, getLineProps, getTokenProps}) => ( -
-          {tokens.map((line, i) => (
-            
- {line.map((token, key) => ( - - ))} -
- ))} -
+
+
+            {tokens.map((line, i) => (
+              
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ +
)}
); diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/styles.module.css index b6d81cf1f7..b8b2a53162 100644 --- a/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/styles.module.css +++ b/packages/docusaurus-theme-live-codeblock/src/theme/CodeBlock/styles.module.css @@ -6,3 +6,31 @@ overflow-wrap: break-word; white-space: pre-wrap; } + +.codeBlockWrapper { + position: relative; +} + +.codeBlockWrapper:hover > .copyButton { + bottom: calc(var(--ifm-pre-padding) - 2px); + visibility: visible; + opacity: 1; +} + +.copyButton { + position: absolute; + right: var(--ifm-pre-padding); + bottom: calc(var(--ifm-pre-padding) - 4px); + padding: 4px 8px; + visibility: hidden; + opacity: 0; + transition: opacity 200ms ease-in-out, visibility 200ms ease-in-out, + bottom 200ms ease-in-out; + border: 1px solid rgb(214, 222, 235); + border-radius: var(--ifm-global-radius); + outline: none; + cursor: pointer; + line-height: 12px; + background: rgb(1, 22, 39); + color: rgb(214, 222, 235); +} diff --git a/yarn.lock b/yarn.lock index c7832e1dba..8762280c56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3799,7 +3799,7 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -clipboard@^2.0.0: +clipboard@^2.0.0, clipboard@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==