UNPKG

@mapbox/mr-ui

Version:

UI components for Mapbox projects

180 lines (177 loc) 7.35 kB
"use strict"; 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(); };