Add copy-to-clipboard-button to all code blocks (#566)

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

Co-authored-by: mrdrogdrog <mr.drogdrog@gmail.com>
Co-authored-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
Philip Molares 2020-09-19 22:24:49 +02:00 committed by GitHub
parent 005c80ff55
commit 8e8190b800
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 156 additions and 72 deletions

View file

@ -0,0 +1,56 @@
import React, { RefObject, useCallback, useEffect, useState } from 'react'
import { Overlay, Tooltip } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { v4 as uuid } from 'uuid'
import { ShowIf } from '../show-if/show-if'
export interface CopyOverlayProps {
content: string
clickComponent: RefObject<HTMLElement>
}
export const CopyOverlay: React.FC<CopyOverlayProps> = ({ content, clickComponent }) => {
useTranslation()
const [showCopiedTooltip, setShowCopiedTooltip] = useState(false)
const [error, setError] = useState(false)
const [tooltipId] = useState<string>(() => uuid())
const copyToClipboard = useCallback((content: string) => {
navigator.clipboard.writeText(content).then(() => {
setError(false)
}).catch(() => {
setError(true)
console.error("couldn't copy")
}).finally(() => {
setShowCopiedTooltip(true)
setTimeout(() => { setShowCopiedTooltip(false) }, 2000)
})
}, [])
useEffect(() => {
if (clickComponent && clickComponent.current) {
clickComponent.current.addEventListener('click', () => copyToClipboard(content))
const clickComponentSaved = clickComponent.current
return () => {
if (clickComponentSaved) {
clickComponentSaved.removeEventListener('click', () => copyToClipboard(content))
}
}
}
}, [clickComponent, copyToClipboard, content])
return (
<Overlay target={clickComponent} show={showCopiedTooltip} placement="top">
{(props) => (
<Tooltip id={`copied_${tooltipId}`} {...props}>
<ShowIf condition={error}>
<Trans i18nKey={'common.copyError'}/>
</ShowIf>
<ShowIf condition={!error}>
<Trans i18nKey={'common.successfullyCopied'}/>
</ShowIf>
</Tooltip>
)}
</Overlay>
)
}

View file

@ -0,0 +1,26 @@
import React, { Fragment, useRef } from 'react'
import { Button } from 'react-bootstrap'
import { Variant } from 'react-bootstrap/types'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../fork-awesome/fork-awesome-icon'
import { CopyOverlay } from '../copy-overlay'
export interface CopyToClipboardButtonProps {
content: string
size?: 'sm' | 'lg'
variant?: Variant
}
export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({ content, size = 'sm', variant = 'dark' }) => {
const { t } = useTranslation()
const button = useRef<HTMLButtonElement>(null)
return (
<Fragment>
<Button ref={button} size={size} variant={variant} title={t('renderer.highlightCode.copyCode')}>
<ForkAwesomeIcon icon='files-o'/>
</Button>
<CopyOverlay content={content} clickComponent={button}/>
</Fragment>
)
}

View file

@ -0,0 +1,28 @@
import React, { Fragment, useRef } from 'react'
import { Button, FormControl, InputGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../fork-awesome/fork-awesome-icon'
import { CopyOverlay } from '../copy-overlay'
export interface CopyableFieldProps {
content: string
}
export const CopyableField: React.FC<CopyableFieldProps> = ({ content }) => {
useTranslation()
const button = useRef<HTMLButtonElement>(null)
return (
<Fragment>
<InputGroup className="my-3">
<FormControl readOnly={true} className={'text-center'} value={content} />
<InputGroup.Append>
<Button variant="outline-secondary" ref={button} title={'Copy'}>
<ForkAwesomeIcon icon='files-o'/>
</Button>
</InputGroup.Append>
</InputGroup>
<CopyOverlay content={content} clickComponent={button}/>
</Fragment>
)
}