You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
carbon/components/CopyMenu.js

144 lines
3.9 KiB
JavaScript

import React from 'react'
import { useRouter } from 'next/router'
import { useCopyTextHandler, useAsyncCallback, useKeyboardListener } from 'actionsack'
import morph from 'morphmorph'
import { COLORS } from '../lib/constants'
import Button from './Button'
import Popout, { managePopout } from './Popout'
import CopySVG from './svg/Copy'
const toIFrame = (url, width, height) =>
`<iframe
src="${location.origin}/embed${url.replace(/^\/\?/, '?')}"
style="width: ${width || 1024}px; height: ${
height || 473
}px; border:0; transform: scale(1); overflow:hidden;"
sandbox="allow-scripts allow-same-origin">
</iframe>
`
const toURL = url => `${location.origin}${url}`
// Medium does not handle asterisks correctly - https://github.com/carbon-app/carbon/issues/1067
const replaceAsterisks = string => string.replace(/\*/g, '%2A')
const toEncodedURL = morph.compose(encodeURI, replaceAsterisks, toURL)
function CopyButton(props) {
return (
<Button
{...props}
hoverColor={COLORS.SECONDARY}
color="rgba(255, 255, 255, 0.7)"
padding="8px"
/>
)
}
function CopyEmbed({ mapper, title }) {
const { asPath } = useRouter()
const text = React.useMemo(() => mapper(asPath), [mapper, asPath])
const { onClick, copied } = useCopyTextHandler(text, { interval: 1000 })
return <CopyButton onClick={onClick}>{copied ? 'Copied!' : title}</CopyButton>
}
const popoutStyle = { width: '140px', right: 0 }
function useClipboardSupport() {
const [isClipboardSupported, setClipboardSupport] = React.useState(false)
React.useEffect(() => {
setClipboardSupport(
window.navigator && window.navigator.clipboard && typeof ClipboardItem === 'function'
)
}, [])
return isClipboardSupported
}
function CopyMenu({ isVisible, toggleVisibility, copyImage, carbonRef }) {
const clipboardSupported = useClipboardSupport()
const [showCopied, { loading: copied }] = useAsyncCallback(
() => new Promise(res => setTimeout(res, 1000))
)
const [copy, { loading }] = useAsyncCallback(async (...args) => {
if (clipboardSupported) {
await copyImage(...args)
showCopied()
}
})
useKeyboardListener('⌘-⇧-c', e => {
e.preventDefault()
copy(e)
})
return (
<div className="copy-menu-container">
<div className="flex">
<Button
center
border
large
padding="0 16px"
margin="0 8px 0 0"
onClick={toggleVisibility}
color={COLORS.SECONDARY}
title="Copy menu"
>
<CopySVG size={16} color={COLORS.SECONDARY} />
</Button>
</div>
<Popout
hidden={!isVisible}
borderColor={COLORS.SECONDARY}
pointerRight="24px"
style={popoutStyle}
>
<div className="copy-row flex">
<span>Copy to clipboard</span>
{clipboardSupported && (
<CopyButton id="export-clipboard" onClick={copy} disabled={loading}>
{loading ? 'Copying…' : copied ? 'Copied!' : 'Image'}
</CopyButton>
)}
<CopyEmbed title="Medium.com" mapper={toEncodedURL} />
<CopyEmbed
title="IFrame"
mapper={url => toIFrame(url, carbonRef.clientWidth, carbonRef.clientHeight)}
/>
<CopyEmbed title="Plain URL" mapper={toURL} />
</div>
</Popout>
<style jsx>
{`
.copy-menu-container {
position: relative;
color: ${COLORS.SECONDARY};
flex: 1;
}
.copy-row {
flex-direction: column;
justify-content: space-between;
border-bottom: 1px solid ${COLORS.SECONDARY};
}
.copy-row :global(button) {
border-top: 1px solid ${COLORS.SECONDARY};
}
.copy-row > span {
padding: 8px;
margin: 0 auto;
}
`}
</style>
</div>
)
}
export default managePopout(React.memo(CopyMenu))