@mapbox/mr-ui
Version:
UI components for Mapbox projects
148 lines (147 loc) • 6.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = Copiable;
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
var _copyButton = _interopRequireDefault(require("../copy-button"));
var _popover = _interopRequireDefault(require("../popover"));
var _osKey = _interopRequireDefault(require("os-key"));
var _select = _interopRequireDefault(require("select"));
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; }
const DISABLE_CLICK_TO_SELECT_THRESHOLD = 640;
const FEEDBACK_TIME = 2000;
function getCopyKeys(ua) {
const keys = (0, _osKey.default)(ua);
if (!keys) return;
if (keys.primaryMeta) {
// ⌘+C
return `${keys.meta.symbol}+C`;
} else {
// Ctrl+C
return `${keys.ctrl.abbreviation}+C`;
}
}
/**
* Make some text easily copiable.
*
* Comes with a [CopyButton](#copybutton) that you can click to automatically
* copy the text.
*
* Also, on wider viewports a click on the text will automatically select it
* all, so you can easily copy with your favorite keyboard shortcut.
*/
function Copiable(_ref) {
let {
value,
focusTrapPaused,
onCopy,
truncated = false
} = _ref;
const textEl = (0, _react.useRef)(null);
const showCopyButton = (0, _react.useRef)(_copyButton.default.isCopySupported());
const [copyTooltipActive, setCopyTooltipActive] = (0, _react.useState)(false);
(0, _react.useEffect)(() => {
let timer;
if (copyTooltipActive) {
timer = setTimeout(() => {
setCopyTooltipActive(false);
}, FEEDBACK_TIME);
}
return () => clearTimeout(timer);
}, [copyTooltipActive]);
const handleTextFocus = () => {
if (typeof window === 'undefined') return;
if (window.innerWidth < DISABLE_CLICK_TO_SELECT_THRESHOLD) return;
(0, _select.default)(() => textEl.current);
setCopyTooltipActive(true);
};
const handleTextBlur = event => {
if (!textEl.current.contains(event.relatedTarget)) {
setCopyTooltipActive(false);
}
};
const renderCopyButton = /*#__PURE__*/_react.default.createElement("div", {
className: "absolute top right px6 py6"
}, /*#__PURE__*/_react.default.createElement(_copyButton.default, {
text: value,
block: true,
focusTrapPaused: focusTrapPaused,
onCopy: onCopy
}));
const renderCopyHintText = () => {
if (typeof window === 'undefined') return;
return /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement("span", {
className: "txt-kbd"
}, getCopyKeys(window.navigator.userAgent)), ' ', "to copy");
};
const textClasses = (0, _classnames.default)('my3 txt-mono txt-s mr24', {
'txt-truncate': truncated
});
const textStyle = {};
if (!truncated) {
textStyle.wordWrap = 'break-word';
textStyle.overflowWrap = 'break-word';
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "relative clearfix bg-darken5 round"
}, showCopyButton.current && renderCopyButton, /*#__PURE__*/_react.default.createElement(_popover.default, {
content: /*#__PURE__*/_react.default.createElement("div", {
className: "txt-s"
}, showCopyButton.current && renderCopyHintText),
active: copyTooltipActive,
placement: "top",
alignment: "center",
hideWhenAnchorIsOffscreen: true,
"aria-label": "Copy the selected text",
padding: "small"
}, /*#__PURE__*/_react.default.createElement("div", {
tabIndex: -1,
ref: textEl,
onFocus: handleTextFocus,
onBlur: handleTextBlur,
className: "py6 px12",
"data-testid": "copiable-text-el"
}, /*#__PURE__*/_react.default.createElement("div", {
className: textClasses,
style: textStyle
}, value))));
}
Copiable.propTypes = {
/**
* The text that will be displayed and copied.
*/
value: _propTypes.default.string.isRequired,
/**
* 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,
/**
* If `false` (default), the text will be overflow to multiple lines,
* and words longer than a single line (e.g. long access tokens or URLs)
* will be broken to enforce wrapping.
*
* If `true`, the Copiable's text will be truncated to a single line
* of text. **Only set this to `true` if you know that your target
* browsers support the copy button!** Some browsers will not effectively
* copy text that is truncated by CSS, so the risk is that some of your
* users might have *no way* to view and copy all the text if the copy
* button does not work for them.
*
* Horizontal scrolling is not an option because of things end up getting
* pretty gross across browsers.
*/
truncated: _propTypes.default.bool,
/**
* Invoked when the button is clicked.
* Passed one argument: the `text` prop.
*/
onCopy: _propTypes.default.func
};