@mapbox/mr-ui
Version:
UI components for Mapbox projects
180 lines (177 loc) • 7.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = CopyButton;
var _react = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _clipboardMin = _interopRequireDefault(require("clipboard/dist/clipboard.min.js"));
var _icon = _interopRequireDefault(require("../icon"));
var _popover = _interopRequireDefault(require("../popover"));
var _tooltip = _interopRequireDefault(require("../tooltip"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const FEEDBACK_TIME = 800;
/**
* A button that, when clicked, copies the designated text to the clipboard.
*
* The static function `CopyButton.isCopySupported` returns a boolean
* indicating whether the current browser will support automatic copying.
* If it does not, you might want to hide your copy button and make sure
* the user is able to manually select and copy the text.
*/
function CopyButton(_ref) {
let {
text,
onCopy,
block = false,
focusTrapPaused,
className = 'btn btn--xs py3 px3 round',
children,
tooltipColoring = 'light',
tooltipTextSize = 's',
passthroughProps
} = _ref;
const [clipboard, setClipboard] = (0, _react.useState)(null);
const [clipboardElement, setClipboardElement] = (0, _react.useState)(null);
const [showingFeedback, setShowingFeedback] = (0, _react.useState)(false);
const makeClipboard = (0, _react.useCallback)(() => {
if (clipboardElement) {
setClipboard(new _clipboardMin.default(clipboardElement, {
// Setting the container is necessary for Clipboard to function within
// focus traps, like in a Modal.
...(!focusTrapPaused && {
container: clipboardElement
})
}));
}
}, [focusTrapPaused, clipboardElement]);
const reinitializeClipboard = () => {
if (clipboard) {
clipboard.destroy();
makeClipboard();
}
};
(0, _react.useEffect)(() => {
return () => {
if (clipboard) {
clipboard.destroy();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
(0, _react.useEffect)(() => {
// initialize clipboard once ref is set
if (clipboardElement && !clipboard) {
makeClipboard();
}
}, [clipboardElement, clipboard, makeClipboard]);
(0, _react.useEffect)(() => {
let timer;
if (showingFeedback) {
timer = setTimeout(() => {
setShowingFeedback(false);
}, FEEDBACK_TIME);
}
return () => clearTimeout(timer);
}, [showingFeedback]);
const handleCopyButtonClick = () => {
// Clipboard.js attaches its own click handlers for copying
if (onCopy) {
onCopy(text);
}
// Show feedback
setShowingFeedback(true);
reinitializeClipboard();
};
const iconName = showingFeedback ? 'check' : 'clipboard';
const buttonClasses = (0, _classnames.default)(className, {
block
});
const body = children ? children : /*#__PURE__*/_react.default.createElement("button", _extends({
type: "button",
className: buttonClasses
}, passthroughProps), /*#__PURE__*/_react.default.createElement(_icon.default, {
name: iconName
}));
let popoverContent = 'Copied!';
if (tooltipTextSize !== 'none') {
popoverContent = /*#__PURE__*/_react.default.createElement("div", {
className: `txt-${tooltipTextSize}`
}, popoverContent);
}
// data-clipboard-text and the container ref are used by clipboard.js
// to copy text. Note that this wont have as nice a failure mode.
return /*#__PURE__*/_react.default.createElement("div", {
role: "button",
"aria-label": "Copy",
"data-testid": "copy-button",
ref: ref => setClipboardElement(ref),
"data-clipboard-text": text,
onClick: handleCopyButtonClick
}, /*#__PURE__*/_react.default.createElement(_popover.default, {
content: popoverContent,
active: showingFeedback,
coloring: tooltipColoring,
placement: "top",
alignment: "center",
hideWhenAnchorIsOffscreen: true,
padding: "small"
}, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_tooltip.default, {
disabled: showingFeedback,
coloring: tooltipColoring,
textSize: tooltipTextSize,
content: "Copy"
}, body))));
}
CopyButton.propTypes = {
/**
* The text that will be copied when the button is clicked.
*/
text: _propTypes.default.string.isRequired,
/**
* Invoked when the button is clicked.
* Passed one argument: the `text` prop.
*/
onCopy: _propTypes.default.func,
/**
* If `true`, the element will be `block` displayed instead of `inline-block`.
*
* This is sometimes necessary to get your pixel-perfect layout, if you don't
* want the extra line-height that wraps inline elements. Typically, you
* should only set `block` to `true` if the parent element is controlling
* width (in a layout that uses flexbox, absolute positioning, or floats).
*/
block: _propTypes.default.bool,
/**
* If `true`, this will allow interaction with elements outside of the
* modal container. You normally don't want to set this, but it can be
* useful for nesting different components that are displaced to other
* parts of the DOM.
*/
focusTrapPaused: _propTypes.default.bool,
/**
* The `className` prop of the `<button>`.
*/
className: _propTypes.default.string,
/**
* An object of props that you want to pass through to the `<button>`.
*/
passthroughProps: _propTypes.default.object,
/**
* Either `'light'` or `'dark'`. This value is passed as the `coloring` prop found in Tooltip and Popover.
*/
tooltipColoring: _propTypes.default.oneOf(['light', 'dark']),
/**
* `'xs'` (extra small), `'s'` (small), or `'none'` (no size class).
*/
tooltipTextSize: _propTypes.default.oneOf(['xs', 's', 'none']),
/** Optional content to represent the button. */
children: _propTypes.default.node
};
CopyButton.isCopySupported = () => {
return typeof window === 'undefined' ? false : _clipboardMin.default.isSupported();
};