@ant-design/x-markdown
Version:
placeholder for @ant-design/x-markdown
313 lines (305 loc) • 12.6 kB
JavaScript
;
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;