Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 71 additions & 123 deletions dist/react-clipboard.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/react-clipboard.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

179 changes: 81 additions & 98 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,125 +1,108 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Clipboard from 'clipboard';

class ClipboardButton extends React.Component {
static propTypes = {
options: function(props, propName, componentName) {
const options = props[propName];
if (options && typeof options !== 'object' || Array.isArray(options)) {
return new Error(`Invalid props '${propName}' supplied to '${componentName}'. ` +
`'${propName}' is not an object.`);
}

if (props['option-text'] !== undefined) {
const optionText = props['option-text'];
if (typeof optionText !== 'function') {
return new Error(`Invalid props 'option-text' supplied to '${componentName}'. ` +
`'option-text' is not a function.`);
}
}
},
title: PropTypes.string,
type: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
component: PropTypes.any,
children: PropTypes.any,
}

static defaultProps = {
isVisibleWhenUnsupported: true,
onClick: function() {},
}
const ClipboardButton = (props) => {
const {component, title, style, isVisibleWhenUnsupported, className, onClick, type} = props;
const ref = React.useRef();

/* Returns a object with all props that fulfill a certain naming pattern
*
* @param {RegExp} regexp - Regular expression representing which pattern
* you'll be searching for.
* @param {Boolean} remove - Determines if the regular expression should be
* removed when transmitting the key from the props
* to the new object.
*
* e.g:
*
* // Considering:
* // this.props = {option-foo: 1, onBar: 2, data-foobar: 3 data-baz: 4};
*
* // *RegExps not using // so that this comment doesn't break up
* this.propsWith(option-*, true); // returns {foo: 1}
* this.propsWith(on*, true); // returns {Bar: 2}
* this.propsWith(data-*); // returns {data-foobar: 1, data-baz: 4}
*/
propsWith(regexp, remove=false) {
function propsWith(regexp, remove=false) {
const object = {};

Object.keys(this.props).forEach(function(key) {
Object.keys(props).forEach((key) => {
if (key.search(regexp) !== -1) {
const objectKey = remove ? key.replace(regexp, '') : key;
object[objectKey] = this.props[key];
object[objectKey] = props[key];
}
}, this);
});

return object;
}

componentWillUnmount() {
this.clipboard && this.clipboard.destroy();
}

componentDidMount() {
// Support old API by trying to assign this.props.options first;
const options = this.props.options || this.propsWith(/^option-/, true);
const element = ReactDOM.findDOMNode(this.element);
React.useEffect(() => {
// Support old API by trying to assign props.options first;
const options = props.options || propsWith(/^option-/, true);
const element = ReactDOM.findDOMNode(ref.current);
if (!element) {
return;
}

const Clipboard = require('clipboard');
this.clipboard = new Clipboard(element, options);

const callbacks = this.propsWith(/^on/, true);
Object.keys(callbacks).forEach(function(callback) {
this.clipboard.on(callback.toLowerCase(), this.props['on' + callback]);
}, this);
}

render() {
const attributes = {
title: this.props.title || '',
type: this.getType(),
className: this.props.className || '',
style: this.props.style || {},
ref: element => this.element = element,
onClick: this.props.onClick,
...this.propsWith(/^data-/),
...this.propsWith(/^button-/, true),

const clipboard = new Clipboard(element, options);

const callbacks = propsWith(/^on/, true);
Object.keys(callbacks).forEach((callback) => {
clipboard.on(callback.toLowerCase(), props['on' + callback]);
});

return () => {
clipboard.destroy();
};
}, []);

const Clipboard = require('clipboard');
if (!this.props.isVisibleWhenUnsupported && !Clipboard.isSupported()) {
return null;
}

return React.createElement(
this.getComponent(),
attributes,
this.props.children
);
}

getType() {
if (this.getComponent() === 'button' || this.getComponent() === 'input') {
return this.props.type || 'button';
function getType() {
const componentType = getComponent();
if (componentType === 'button' || componentType === 'input') {
return type || 'button';
}
else {
return undefined;
}
}

getComponent() {
return this.props.component || 'button';
function getComponent() {
return component || 'button';
}
}

export default ClipboardButton;
const attributes = {
title: title || '',
type: getType(),
className: className || '',
style: style || {},
ref,
onClick: onClick,
...propsWith(/^data-/),
...propsWith(/^button-/, true),
};

if (!isVisibleWhenUnsupported && !Clipboard.isSupported()) {
return null;
}

return React.createElement(
getComponent(),
attributes,
props.children
);
};

ClipboardButton.propTypes = {
options: function(props, propName, componentName) {
const options = props[propName];
if (options && typeof options !== 'object' || Array.isArray(options)) {
return new Error(`Invalid props '${propName}' supplied to '${componentName}'. ` +
`'${propName}' is not an object.`);
}

if (props['option-text'] !== undefined) {
const optionText = props['option-text'];
if (typeof optionText !== 'function') {
return new Error(`Invalid props 'option-text' supplied to '${componentName}'. ` +
`'option-text' is not a function.`);
}
}
},
title: PropTypes.string,
type: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
component: PropTypes.any,
children: PropTypes.any,
};

ClipboardButton.defaultProps = {
isVisibleWhenUnsupported: true,
onClick: function() {},
};

export default ClipboardButton;
Loading