/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React from 'react'; import PropTypes from 'prop-types'; import { Tooltip, OverlayTrigger, MenuItem } from 'react-bootstrap'; import { t } from '@superset-ui/translation'; import withToasts from 'src/messageToasts/enhancers/withToasts'; const propTypes = { copyNode: PropTypes.node, getText: PropTypes.func, onCopyEnd: PropTypes.func, shouldShowText: PropTypes.bool, text: PropTypes.string, inMenu: PropTypes.bool, wrapped: PropTypes.bool, tooltipText: PropTypes.string, addDangerToast: PropTypes.func.isRequired, }; const defaultProps = { copyNode: Copy, onCopyEnd: () => {}, shouldShowText: true, inMenu: false, wrapped: true, tooltipText: t('Copy to clipboard'), }; class CopyToClipboard extends React.Component { constructor(props) { super(props); this.state = { hasCopied: false, }; // bindings this.copyToClipboard = this.copyToClipboard.bind(this); this.resetTooltipText = this.resetTooltipText.bind(this); this.onMouseOut = this.onMouseOut.bind(this); this.onClick = this.onClick.bind(this); } onMouseOut() { // delay to avoid flash of text change on tooltip setTimeout(this.resetTooltipText, 200); } onClick() { if (this.props.getText) { this.props.getText(d => { this.copyToClipboard(d); }); } else { this.copyToClipboard(this.props.text); } } resetTooltipText() { this.setState({ hasCopied: false }); } copyToClipboard(textToCopy) { const selection = document.getSelection(); selection.removeAllRanges(); document.activeElement.blur(); const range = document.createRange(); const span = document.createElement('span'); span.textContent = textToCopy; span.style.all = 'unset'; span.style.position = 'fixed'; span.style.top = 0; span.style.clip = 'rect(0, 0, 0, 0)'; span.style.whiteSpace = 'pre'; document.body.appendChild(span); range.selectNode(span); selection.addRange(range); try { if (!document.execCommand('copy')) { throw new Error(t('Not successful')); } } catch (err) { this.props.addDangerToast( t('Sorry, your browser does not support copying. Use Ctrl / Cmd + C!'), ); } document.body.removeChild(span); if (selection.removeRange) { selection.removeRange(range); } else { selection.removeAllRanges(); } this.setState({ hasCopied: true }); this.props.onCopyEnd(); } tooltipText() { if (this.state.hasCopied) { return t('Copied!'); } return this.props.tooltipText; } renderNotWrapped() { const { copyNode } = this.props; return ( {copyNode} ); } renderLink() { return ( {this.props.shouldShowText && this.props.text && ( {this.props.text} )} {this.props.copyNode} ); } renderInMenu() { return ( {this.props.copyNode} ); } renderTooltip() { return ( {this.tooltipText()} ); } render() { const { wrapped, inMenu } = this.props; if (!wrapped) { return this.renderNotWrapped(); } return inMenu ? this.renderInMenu() : this.renderLink(); } } export default withToasts(CopyToClipboard); CopyToClipboard.propTypes = propTypes; CopyToClipboard.defaultProps = defaultProps;