react-code-view
Version:
Code view for React
100 lines (95 loc) • 4.66 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports["default"] = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _react = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _copyToClipboard = _interopRequireDefault(require("copy-to-clipboard"));
var _mergeRefs = _interopRequireDefault(require("./utils/mergeRefs"));
var _Copy = require("./icons/Copy");
var _Check = require("./icons/Check");
var _jsxRuntime = require("react/jsx-runtime");
var _excluded = ["className"],
_excluded2 = ["children", "className", "copyButtonProps"];
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
/**
* Creates and appends a copy button to a code container
* @param container - The container element to append the copy button to
* @param buttonProps - Additional props to apply to the copy button
*/
function createCopyButton(container, buttonProps) {
// If the container is null or the container already has a copy button, return
if (!container || container.querySelector('button[data-type="copy"]')) {
return;
}
var _ref = buttonProps || {},
className = _ref.className,
rest = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
var button = document.createElement('button');
button.dataset['type'] = 'copy';
button.title = 'Copy code';
button.setAttribute('aria-label', 'Copy code');
button.innerHTML = (0, _Copy.svgTpl)(_Copy.iconPath);
if (className) {
button.className = className;
}
button.onclick = function (e) {
var _container$querySelec;
e.preventDefault();
var code = (_container$querySelec = container.querySelector('code')) === null || _container$querySelec === void 0 ? void 0 : _container$querySelec.textContent;
var icon = button.querySelector('.copy-icon-path');
// Show check icon to indicate successful copy
icon === null || icon === void 0 ? void 0 : icon.setAttribute('d', _Check.iconPath);
if (code) {
(0, _copyToClipboard["default"])(code);
}
// Reset to copy icon after 2 seconds
setTimeout(function () {
icon === null || icon === void 0 ? void 0 : icon.setAttribute('d', _Copy.iconPath);
}, 2000);
};
// Apply additional button properties
if (rest) {
Object.entries(rest).forEach(function (_ref2) {
var key = _ref2[0],
value = _ref2[1];
if (value !== undefined) {
button.setAttribute(key, String(value));
}
});
}
container.appendChild(button);
}
/**
* Renders markdown content with code blocks that have copy buttons
*/
var MarkdownRenderer = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
var children = props.children,
className = props.className,
copyButtonProps = props.copyButtonProps,
rest = (0, _objectWithoutPropertiesLoose2["default"])(props, _excluded2);
var mdRef = (0, _react.useRef)(null);
(0, _react.useEffect)(function () {
var _mdRef$current;
// Add copy buttons to all code blocks
var codeBlocks = (_mdRef$current = mdRef.current) === null || _mdRef$current === void 0 ? void 0 : _mdRef$current.querySelectorAll('.rcv-code-renderer');
codeBlocks === null || codeBlocks === void 0 ? void 0 : codeBlocks.forEach(function (codeBlock) {
createCopyButton(codeBlock, copyButtonProps);
});
// We only want to run this once when the component mounts
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!children) {
return null;
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", (0, _extends2["default"])({}, rest, {
ref: (0, _mergeRefs["default"])(mdRef, ref),
className: (0, _classnames["default"])(className, 'rcv-markdown'),
dangerouslySetInnerHTML: {
__html: children
}
}));
});
var _default = exports["default"] = MarkdownRenderer;