UNPKG

@ant-design/x-markdown

Version:

placeholder for @ant-design/x-markdown

313 lines (305 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _icons = require("@ant-design/icons"); var _useXComponentConfig = _interopRequireDefault(require("@ant-design/x/es/_util/hooks/use-x-component-config")); var _useLocale = _interopRequireDefault(require("@ant-design/x/es/locale/useLocale")); var _useXProviderContext = _interopRequireDefault(require("@ant-design/x/es/x-provider/hooks/use-x-provider-context")); var _en_US = _interopRequireDefault(require("@ant-design/x/locale/en_US")); var _antd = require("antd"); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = _interopRequireDefault(require("lodash.throttle")); var _mermaid = _interopRequireDefault(require("mermaid")); var _react = _interopRequireWildcard(require("react")); var _reactSyntaxHighlighter = _interopRequireDefault(require("react-syntax-highlighter")); var _style = _interopRequireDefault(require("./style")); 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 _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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); } var RenderType = /*#__PURE__*/function (RenderType) { RenderType["Code"] = "code"; RenderType["Image"] = "image"; return RenderType; }(RenderType || {}); _mermaid.default.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'default', fontFamily: 'monospace' }); let uuid = 0; const Mermaid = /*#__PURE__*/_react.default.memo(props => { const { prefixCls: customizePrefixCls, className, style, classNames = {}, styles = {}, header, children, highlightProps } = props; const [renderType, setRenderType] = (0, _react.useState)(RenderType.Image); const [scale, setScale] = (0, _react.useState)(1); const [position, setPosition] = (0, _react.useState)({ x: 0, y: 0 }); const [isDragging, setIsDragging] = (0, _react.useState)(false); const [lastMousePos, setLastMousePos] = (0, _react.useState)({ x: 0, y: 0 }); const containerRef = (0, _react.useRef)(null); const id = `mermaid-${uuid++}-${children?.length || 0}`; const [messageApi, contextHolder] = _antd.message.useMessage(); // ============================ locale ============================ const [contextLocale] = (0, _useLocale.default)('Mermaid', _en_US.default.Mermaid); // ============================ Prefix ============================ const { getPrefixCls, direction } = (0, _useXProviderContext.default)(); const prefixCls = getPrefixCls('mermaid', customizePrefixCls); const [hashId, cssVarCls] = (0, _style.default)(prefixCls); // ===================== Component Config ========================= const contextConfig = (0, _useXComponentConfig.default)('mermaid'); // ============================ style ============================ const mergedCls = (0, _classnames.default)(prefixCls, contextConfig.className, contextConfig.classNames.root, className, classNames.root, hashId, cssVarCls, { [`${prefixCls}-rtl`]: direction === 'rtl' }); // ============================ render mermaid ============================ const renderDiagram = (0, _lodash.default)(async () => { if (!children || !containerRef.current || renderType === RenderType.Code) return; try { const isValid = await _mermaid.default.parse(children, { suppressErrors: true }); if (!isValid) throw new Error('Invalid Mermaid syntax'); const newText = children.replace(/[`\s]+$/g, ''); const { svg } = await _mermaid.default.render(id, newText, containerRef.current); containerRef.current.innerHTML = svg; } catch (error) { console.warn(`Mermaid render failed: ${error}`); } }, 100); (0, _react.useEffect)(() => { if (renderType === RenderType.Code && containerRef.current) { // 清理图表内容,避免在代码视图下出现渲染错误 containerRef.current.innerHTML = ''; } else { renderDiagram(); } }, [children, renderType]); (0, _react.useEffect)(() => { const container = containerRef.current; if (!container || renderType !== RenderType.Image) return; let lastTime = 0; const wheelHandler = e => { e.preventDefault(); e.stopPropagation(); const now = Date.now(); if (now - lastTime < 16) return; lastTime = now; const delta = e.deltaY > 0 ? -0.1 : 0.1; setScale(prev => Math.max(0.5, Math.min(3, prev + delta))); }; container.addEventListener('wheel', wheelHandler, { passive: false }); return () => { container.removeEventListener('wheel', wheelHandler); }; }, [renderType]); (0, _react.useEffect)(() => { if (containerRef.current && renderType === RenderType.Image) { const svg = containerRef.current.querySelector('svg'); if (svg) { svg.style.transform = `scale(${scale}) translate(${position.x}px, ${position.y}px)`; svg.style.transformOrigin = 'center'; svg.style.transition = isDragging ? 'none' : 'transform 0.1s ease-out'; svg.style.cursor = isDragging ? 'grabbing' : 'grab'; } } }, [scale, position, renderType, isDragging]); // 鼠标拖动事件处理 const handleMouseDown = e => { if (renderType !== RenderType.Image) return; e.preventDefault(); setIsDragging(true); setLastMousePos({ x: e.clientX, y: e.clientY }); }; const handleMouseMove = e => { if (!isDragging || renderType !== RenderType.Image) return; e.preventDefault(); const deltaX = e.clientX - lastMousePos.x; const deltaY = e.clientY - lastMousePos.y; setPosition(prev => ({ x: prev.x + deltaX / scale, y: prev.y + deltaY / scale })); setLastMousePos({ x: e.clientX, y: e.clientY }); }; const handleMouseUp = () => { setIsDragging(false); }; const handleReset = () => { setScale(1); setPosition({ x: 0, y: 0 }); }; // ============================ render content ============================ if (!children) { return null; } const handleDownload = async () => { const svgElement = containerRef.current?.querySelector('svg'); if (!svgElement) return; const svgString = new XMLSerializer().serializeToString(svgElement); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return; const { width, height } = svgElement.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; ctx.scale(dpr, dpr); const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0, width, height); const link = document.createElement('a'); link.download = `${Date.now()}.png`; link.href = canvas.toDataURL('image/png', 1); link.click(); }; img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`; }; const handleZoomIn = () => { setScale(prev => Math.min(prev + 0.2, 3)); }; const handleZoomOut = () => { setScale(prev => Math.max(prev - 0.2, 0.5)); }; const handleCopyCode = async () => { if (!children) return; try { await navigator.clipboard.writeText(children.trim()); messageApi.open({ type: 'success', content: contextLocale.copySuccess }); } catch (error) { console.error('Failed to copy code:', error); } }; const renderHeader = () => { if (header === null) return null; if (header) return header; return /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)(`${prefixCls}-header`, contextConfig.classNames.header, classNames?.header), style: { ...contextConfig.styles.header, ...styles.header } }, contextHolder, /*#__PURE__*/_react.default.createElement(_antd.Segmented, { options: [{ label: contextLocale.image, value: RenderType.Image }, { label: contextLocale.code, value: RenderType.Code }], value: renderType, onChange: setRenderType }), /*#__PURE__*/_react.default.createElement(_antd.Space, null, /*#__PURE__*/_react.default.createElement(_antd.Tooltip, { title: contextLocale.copy }, /*#__PURE__*/_react.default.createElement(_antd.Button, { type: "text", size: "small", icon: /*#__PURE__*/_react.default.createElement(_icons.CopyOutlined, null), onClick: handleCopyCode })), renderType === RenderType.Image ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_antd.Tooltip, { title: contextLocale.zoomOut }, /*#__PURE__*/_react.default.createElement(_antd.Button, { type: "text", size: "small", icon: /*#__PURE__*/_react.default.createElement(_icons.ZoomInOutlined, null), onClick: handleZoomIn })), /*#__PURE__*/_react.default.createElement(_antd.Tooltip, { title: contextLocale.zoomIn }, /*#__PURE__*/_react.default.createElement(_antd.Button, { type: "text", size: "small", icon: /*#__PURE__*/_react.default.createElement(_icons.ZoomOutOutlined, null), onClick: handleZoomOut })), /*#__PURE__*/_react.default.createElement(_antd.Tooltip, { title: contextLocale.zoomReset }, /*#__PURE__*/_react.default.createElement(_antd.Button, { type: "text", size: "small", onClick: handleReset }, contextLocale.zoomReset)), /*#__PURE__*/_react.default.createElement(_antd.Tooltip, { title: contextLocale.download }, /*#__PURE__*/_react.default.createElement(_antd.Button, { type: "text", size: "small", icon: /*#__PURE__*/_react.default.createElement(_icons.DownloadOutlined, null), onClick: handleDownload }))) : null)); }; const renderContent = () => { return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)(`${prefixCls}-graph`, contextConfig.classNames.graph, renderType === RenderType.Code && `${prefixCls}-graph-hidden`, classNames?.graph), style: { ...contextConfig.styles.graph, ...styles.graph }, ref: containerRef, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp }), renderType === RenderType.Code ? /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)(`${prefixCls}-code`, contextConfig.classNames.code, classNames.code), style: { ...contextConfig.styles.code, ...styles.code } }, /*#__PURE__*/_react.default.createElement(_reactSyntaxHighlighter.default, _extends({ customStyle: { padding: 0, background: 'transparent' }, language: "mermaid", wrapLines: true }, highlightProps), children.replace(/\n$/, ''))) : null); }; return /*#__PURE__*/_react.default.createElement("div", { className: mergedCls, style: { ...style, ...contextConfig.style, ...contextConfig.styles.root, ...styles.root } }, renderHeader(), renderContent()); }); var _default = exports.default = Mermaid;