Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add button to copy data to clipboard #338

Merged
merged 2 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"array-move": "^3.0.1",
"bootstrap": "^4.5.0",
"classnames": "^2.2.6",
"clipboard": "^2.0.8",
"comlink": "^4.3.0",
"d3": "^6.5.0",
"dayjs": "^1.8.33",
Expand Down
42 changes: 42 additions & 0 deletions src/components/CopyToClipboardButton/CopyToClipboardButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useCallback, useState } from 'react'
import { Button } from 'react-bootstrap'
import { BsClipboard } from 'react-icons/bs'
import { IoMdCheckmarkCircle } from 'react-icons/io'
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard'
import style from './style.module.css'

export function CopyToClipboardButton({ content }) {
const copyToClipboard = useCopyToClipboard()
const [pending, setPending] = useState(false)

const handleCopy = useCallback(() => {
if (!pending) {
setPending(true)
copyToClipboard(content)
setTimeout(() => {
setPending(false)
}, 3000)
}
}, [content, copyToClipboard, pending])

return (
<Button
variant="light"
className={style['copy-to-clipboard-button'] + " d-flex flex-row align-items-center"}
onClick={handleCopy}
>
{pending && (
<>
<IoMdCheckmarkCircle className="text-success" />
<span className="ml-2">Copied to clipboard</span>
</>
)}
{!pending && (
<>
<BsClipboard />
<span className="ml-2">Copy to clipboard</span>
</>
)}
</Button>
)
}
1 change: 1 addition & 0 deletions src/components/CopyToClipboardButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './CopyToClipboardButton'
3 changes: 3 additions & 0 deletions src/components/CopyToClipboardButton/style.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.copy-to-clipboard-button {
background-color: #f5f8fa;
}
20 changes: 18 additions & 2 deletions src/components/DataLoader/DataLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import Loading from './loading'
import WarningMessage from '../WarningMessage'
import DataMismatchModal from './DataMismatchModal'
import SparqlFetch from './loaders/SparqlFetch'
import { tsvFormat } from 'd3-dsv'
import { CopyToClipboardButton } from '../CopyToClipboardButton'

function DataLoader({
userInput,
Expand Down Expand Up @@ -244,6 +246,10 @@ function DataLoader({
window.location.replace(window.location.pathname)
}, [])

const copyToClipboardButton = !!userData ? (
<CopyToClipboardButton content={tsvFormat(userData)} />
) : null

return (
<>
<Row>
Expand Down Expand Up @@ -381,22 +387,32 @@ function DataLoader({
chart!
</span>
}
action={copyToClipboardButton}
/>
)}

{parseError && (
<WarningMessage variant="danger" message={parseError} />
<WarningMessage
variant="danger"
message={parseError}
action={copyToClipboardButton}
/>
)}

{get(data, 'errors', []).length > 0 && (
<WarningMessage
variant="warning"
message={parsingErrors(data)}
action={copyToClipboardButton}
/>
)}

{loadingError && (
<WarningMessage variant="danger" message={loadingError} />
<WarningMessage
variant="danger"
message={loadingError}
action={copyToClipboardButton}
/>
)}
</Col>
</Row>
Expand Down
12 changes: 10 additions & 2 deletions src/components/WarningMessage/WarningMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ import { Alert } from 'react-bootstrap'
* @param {string} type The warning type: success, secondary, warning, danger
* @param {string} error The error message to be displayed
*/
function WarningMessage({ variant = "warning", message = "A default warning message."}) {
function WarningMessage({
variant = 'warning',
message = 'A default warning message.',
action = null,
}) {
return (
<Alert variant={variant} className="my-2">
<Alert
variant={variant}
className="my-2 d-flex flex-row justify-content-between align-items-center"
>
{message}
{action}
</Alert>
)
}
Expand Down
20 changes: 20 additions & 0 deletions src/hooks/useCopyToClipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import ClipboardJS from "clipboard";
import { useCallback } from "react";


export function useCopyToClipboard() {
return useCallback((str) => {
const btn = document.createElement("button")
btn.style.display = "none"
document.body.appendChild(btn)
const clipboard = new ClipboardJS(btn, {
text: function() {
return str
}
})
btn.focus()
btn.click()
clipboard.destroy()
document.body.removeChild(btn)
}, [])
}
31 changes: 31 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3863,6 +3863,15 @@ 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.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==
dependencies:
good-listener "^1.2.2"
select "^1.1.2"
tiny-emitter "^2.0.0"

cliui@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
Expand Down Expand Up @@ -5296,6 +5305,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=

delegate@^3.1.2:
version "3.2.0"
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==

delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
Expand Down Expand Up @@ -6682,6 +6696,13 @@ globule@^1.0.0:
lodash "~4.17.10"
minimatch "~3.0.2"

good-listener@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
dependencies:
delegate "^3.1.2"

graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
Expand Down Expand Up @@ -11807,6 +11828,11 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=

select@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=

selfsigned@^1.10.7:
version "1.10.7"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b"
Expand Down Expand Up @@ -12763,6 +12789,11 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=

tiny-emitter@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==

tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
Expand Down