diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e739a09c6..95caab3b62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `EuiCopy` ([#1112](https://github.com/elastic/eui/pull/1112)) - Added `disabled` to `EuiRadioGroup.options` ([#1111](https://github.com/elastic/eui/pull/1111)) **Bug fixes** diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index f2de5c15871..2f76fc32321 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -87,6 +87,9 @@ import { ComboBoxExample } import { ContextMenuExample } from './views/context_menu/context_menu_example'; +import { CopyExample } + from './views/copy/copy_example'; + import { DatePickerExample } from './views/date_picker/date_picker_example'; @@ -377,6 +380,7 @@ const navigation = [{ name: 'Utilities', items: [ AccessibilityExample, + CopyExample, ResponsiveExample, DelayHideExample, ErrorBoundaryExample, diff --git a/src-docs/src/views/copy/copy.js b/src-docs/src/views/copy/copy.js new file mode 100644 index 00000000000..ee61773db9f --- /dev/null +++ b/src-docs/src/views/copy/copy.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; + +import { + EuiCopy, + EuiButton, + EuiFieldText, + EuiSpacer, +} from '../../../../src/components/'; + +export default class extends Component { + + state = { + copyText: 'I am the text that will be copied' + } + + onChange = e => { + this.setState({ + copyText: e.target.value, + }); + }; + + render() { + return ( +
+ + + + + + {(copy) => ( + + Click to copy input text + + )} + +
+ ); + } +} diff --git a/src-docs/src/views/copy/copy_example.js b/src-docs/src/views/copy/copy_example.js new file mode 100644 index 00000000000..e6954463f82 --- /dev/null +++ b/src-docs/src/views/copy/copy_example.js @@ -0,0 +1,38 @@ +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { + GuideSectionTypes, +} from '../../components'; + +import { + EuiCode, + EuiCopy, +} from '../../../../src/components'; + +import Copy from './copy'; +const copySource = require('!!raw-loader!./copy'); +const copyHtml = renderToHtml(Copy); + +export const CopyExample = { + title: 'Copy', + sections: [{ + source: [{ + type: GuideSectionTypes.JS, + code: copySource, + }, { + type: GuideSectionTypes.HTML, + code: copyHtml, + }], + text: ( +

+ The EuiCopy component is a utility for copying text to clipboard. + Wrap a function that returns a Component. The first argument will be a `copy` function. +

+ ), + components: { EuiCopy }, + demo: , + props: { EuiCopy }, + }], +}; diff --git a/src/components/copy/copy.js b/src/components/copy/copy.js new file mode 100644 index 00000000000..a505632f742 --- /dev/null +++ b/src/components/copy/copy.js @@ -0,0 +1,80 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { copyToClipboard } from '../../services'; +import { EuiToolTip } from '../tool_tip'; + +export class EuiCopy extends React.Component { + + constructor(props) { + super(props); + + this.state = { + tooltipText: this.props.beforeMessage + }; + } + + copy = () => { + const isCopied = copyToClipboard(this.props.textToCopy); + if (isCopied) { + this.setState({ + tooltipText: this.props.afterMessage, + }); + } + } + + resetTooltipText = () => { + this.setState({ + tooltipText: this.props.beforeMessage, + }); + } + + render() { + const { + children, + textToCopy, // eslint-disable-line no-unused-vars + beforeMessage, // eslint-disable-line no-unused-vars + afterMessage, // eslint-disable-line no-unused-vars + ...rest + } = this.props; + + return ( + + {children(this.copy)} + + ); + } +} + +EuiCopy.propTypes = { + + /** + * Text that will be copied to clipboard when copy function is executed. + */ + textToCopy: PropTypes.string.isRequired, + + /** + * Tooltip message displayed before copy function is called. + */ + beforeMessage: PropTypes.string, + + /** + * Tooltip message displayed after copy function is called that lets the user know that + * 'textToCopy' has been copied to the clipboard. + */ + afterMessage: PropTypes.string.isRequired, + + /** + * Function that must return a Component. First argument is 'copy' function. + * Use your own logic to create the component that user's interactact with when triggering copy. + */ + children: PropTypes.func.isRequired, +}; + +EuiCopy.defaultProps = { + afterMessage: 'Copied', +}; + diff --git a/src/components/copy/index.js b/src/components/copy/index.js new file mode 100644 index 00000000000..fdcffd58765 --- /dev/null +++ b/src/components/copy/index.js @@ -0,0 +1,3 @@ +export { + EuiCopy, +} from './copy'; diff --git a/src/components/index.js b/src/components/index.js index 4148d1ab284..0213f6fe84f 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -65,6 +65,10 @@ export { EuiContextMenuItem, } from './context_menu'; +export { + EuiCopy, +} from './copy'; + export { EuiDatePicker, EuiDatePickerRange, diff --git a/src/components/tool_tip/tool_tip.js b/src/components/tool_tip/tool_tip.js index aa6b1ac0155..21f58ef27cc 100644 --- a/src/components/tool_tip/tool_tip.js +++ b/src/components/tool_tip/tool_tip.js @@ -116,6 +116,10 @@ export class EuiToolTip extends Component { this.hideToolTip(); } } + + if (this.props.onMouseOut) { + this.props.onMouseOut(); + } }; render() { @@ -142,7 +146,7 @@ export class EuiToolTip extends Component { ); let tooltip; - if (visible) { + if (visible && (content || title)) { tooltip = (