UNPKG

react-code-view

Version:
94 lines (90 loc) 3.56 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; var _excluded = ["className"], _excluded2 = ["children", "className", "copyButtonProps"]; import React, { useEffect, useRef, forwardRef } from 'react'; import classNames from 'classnames'; import copy from 'copy-to-clipboard'; import mergeRefs from './utils/mergeRefs'; import { iconPath as copyIconPath, svgTpl } from './icons/Copy'; import { iconPath as checkIconPath } from './icons/Check'; import { jsx as _jsx } from "react/jsx-runtime"; /** * 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 = _objectWithoutPropertiesLoose(_ref, _excluded); var button = document.createElement('button'); button.dataset['type'] = 'copy'; button.title = 'Copy code'; button.setAttribute('aria-label', 'Copy code'); button.innerHTML = svgTpl(copyIconPath); 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', checkIconPath); if (code) { copy(code); } // Reset to copy icon after 2 seconds setTimeout(function () { icon === null || icon === void 0 ? void 0 : icon.setAttribute('d', copyIconPath); }, 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__*/forwardRef(function (props, ref) { var children = props.children, className = props.className, copyButtonProps = props.copyButtonProps, rest = _objectWithoutPropertiesLoose(props, _excluded2); var mdRef = useRef(null); 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__*/_jsx("div", _extends({}, rest, { ref: mergeRefs(mdRef, ref), className: classNames(className, 'rcv-markdown'), dangerouslySetInnerHTML: { __html: children } })); }); export default MarkdownRenderer;