|
@@ -1,91 +1,68 @@
|
|
|
import React from 'react';
|
|
|
-import ReactDOM from 'react-dom';
|
|
|
-import Clip from 'clipboard';
|
|
|
+import copy from 'copy-text-to-clipboard';
|
|
|
|
|
|
import {addErrorMessage, addSuccessMessage} from 'app/actionCreators/indicator';
|
|
|
-
|
|
|
-type DefaultProps = {
|
|
|
- successMessage: string;
|
|
|
- errorMessage: string;
|
|
|
- hideMessages: boolean;
|
|
|
-};
|
|
|
+import {t} from 'app/locale';
|
|
|
|
|
|
type Props = {
|
|
|
+ children: React.ReactElement<{onClick: () => void}>;
|
|
|
+ /** Text to be copied on click */
|
|
|
value: string;
|
|
|
+ successMessage?: string;
|
|
|
+ errorMessage?: string;
|
|
|
+ hideMessages?: boolean;
|
|
|
+ /** Hide children if browser does not support copy */
|
|
|
hideUnsupported?: boolean;
|
|
|
onSuccess?: () => void;
|
|
|
onError?: () => void;
|
|
|
-} & DefaultProps;
|
|
|
-
|
|
|
-class Clipboard extends React.Component<Props> {
|
|
|
- static defaultProps: DefaultProps = {
|
|
|
- hideMessages: false,
|
|
|
- successMessage: 'Copied to clipboard',
|
|
|
- errorMessage: 'Error copying to clipboard',
|
|
|
- };
|
|
|
-
|
|
|
- componentWillUnmount() {
|
|
|
- if (this.clipboard) {
|
|
|
- this.clipboard.destroy();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- clipboard!: ClipboardJS;
|
|
|
-
|
|
|
- handleMount = (ref: HTMLElement) => {
|
|
|
- if (!ref) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const {hideMessages, successMessage, errorMessage, onSuccess, onError} = this.props;
|
|
|
- const hasSuccessCb = typeof onSuccess === 'function';
|
|
|
- const hasErrorCb = typeof onError === 'function';
|
|
|
- const bindEventHandlers = !hideMessages || hasSuccessCb || hasErrorCb;
|
|
|
+};
|
|
|
|
|
|
- // eslint-disable-next-line react/no-find-dom-node
|
|
|
- this.clipboard = new Clip(ReactDOM.findDOMNode(ref) as Element, {
|
|
|
- text: () => this.props.value,
|
|
|
- });
|
|
|
+/**
|
|
|
+ * copy-text-to-clipboard relies on `document.execCommand('copy')`
|
|
|
+ */
|
|
|
+function isSupported() {
|
|
|
+ const support = !!document.queryCommandSupported;
|
|
|
+ return support && !!document.queryCommandSupported('copy');
|
|
|
+}
|
|
|
|
|
|
- if (!bindEventHandlers) {
|
|
|
+function Clipboard({
|
|
|
+ children,
|
|
|
+ value,
|
|
|
+ successMessage = t('Copied to clipboard'),
|
|
|
+ errorMessage = t('Error copying to clipboard'),
|
|
|
+ hideMessages = false,
|
|
|
+ hideUnsupported,
|
|
|
+ onSuccess,
|
|
|
+ onError,
|
|
|
+}: Props) {
|
|
|
+ function handleClick() {
|
|
|
+ // Copy returns whether it succeeded to copy the text
|
|
|
+ const success = copy(value);
|
|
|
+ if (!success) {
|
|
|
+ if (!hideMessages) {
|
|
|
+ addErrorMessage(errorMessage);
|
|
|
+ }
|
|
|
+ onError?.();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- this.clipboard
|
|
|
- .on('success', () => {
|
|
|
- if (!hideMessages) {
|
|
|
- addSuccessMessage(successMessage);
|
|
|
- }
|
|
|
- if (onSuccess && hasSuccessCb) {
|
|
|
- onSuccess();
|
|
|
- }
|
|
|
- })
|
|
|
- .on('error', () => {
|
|
|
- if (!hideMessages) {
|
|
|
- addErrorMessage(errorMessage);
|
|
|
- }
|
|
|
- if (onError && hasErrorCb) {
|
|
|
- onError();
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- render() {
|
|
|
- const {children, hideUnsupported} = this.props;
|
|
|
-
|
|
|
- // Browser doesn't support `execCommand`
|
|
|
- if (hideUnsupported && !Clip.isSupported()) {
|
|
|
- return null;
|
|
|
+ if (!hideMessages) {
|
|
|
+ addSuccessMessage(successMessage);
|
|
|
}
|
|
|
+ onSuccess?.();
|
|
|
+ }
|
|
|
|
|
|
- if (!React.isValidElement(children)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ if (hideUnsupported && !isSupported()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- return React.cloneElement(children, {
|
|
|
- ref: this.handleMount,
|
|
|
- });
|
|
|
+ if (!React.isValidElement(children)) {
|
|
|
+ return null;
|
|
|
}
|
|
|
+
|
|
|
+ return React.cloneElement(children, {
|
|
|
+ onClick: handleClick,
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
export default Clipboard;
|