This commit is contained in:
90
miniprogram/src/components/MarkdownReader/index.tsx
Normal file
90
miniprogram/src/components/MarkdownReader/index.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { View, RichText } from '@tarojs/components'
|
||||
import { marked, Renderer } from 'marked'
|
||||
import CodeBlock from './CodeBlock'
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
content: string
|
||||
themeColor?: string
|
||||
}
|
||||
|
||||
const MarkdownReader: React.FC<Props> = ({ content, themeColor = '#00b96b' }) => {
|
||||
const elements = useMemo(() => {
|
||||
if (!content) return []
|
||||
|
||||
const tokens = marked.lexer(content)
|
||||
const result: React.ReactNode[] = []
|
||||
let currentTokens: any[] = []
|
||||
|
||||
// Configure renderer
|
||||
const renderer = new Renderer()
|
||||
|
||||
renderer.table = (header, body) => {
|
||||
return `<div style="overflow-x: auto; width: 100%; -webkit-overflow-scrolling: touch;">
|
||||
<table style="width: 100%; min-width: 600px; border-collapse: collapse; margin: 16px 0; font-size: 14px;">
|
||||
<thead>${header}</thead>
|
||||
<tbody>${body}</tbody>
|
||||
</table>
|
||||
</div>`
|
||||
}
|
||||
|
||||
renderer.tablecell = (content, flags) => {
|
||||
const type = flags.header ? 'th' : 'td'
|
||||
const style = [
|
||||
'border: 1px solid rgba(255,255,255,0.1)',
|
||||
'padding: 10px',
|
||||
flags.header ? 'background-color: rgba(255,255,255,0.05); font-weight: 700; color: #fff;' : 'color: #ddd;',
|
||||
flags.align ? `text-align: ${flags.align}` : 'text-align: left'
|
||||
].join(';')
|
||||
return `<${type} style="${style}">${content}</${type}>`
|
||||
}
|
||||
|
||||
renderer.image = (href, title, text) => {
|
||||
return `<img src="${href}" style="max-width:100%;border-radius:8px;margin:10px 0;box-shadow: 0 4px 12px rgba(0,0,0,0.3);" title="${title || ''}" alt="${text || ''}" />`
|
||||
}
|
||||
|
||||
renderer.link = (href, title, text) => {
|
||||
return `<a href="${href}" style="color: ${themeColor}; text-decoration: none;">${text}</a>`
|
||||
}
|
||||
|
||||
// Process tokens
|
||||
tokens.forEach((token, index) => {
|
||||
if (token.type === 'code') {
|
||||
// Flush accumulated tokens
|
||||
if (currentTokens.length > 0) {
|
||||
// preserve links if any
|
||||
(currentTokens as any).links = (tokens as any).links
|
||||
const html = marked.parser(currentTokens as any, { renderer, breaks: true })
|
||||
result.push(<RichText key={`rt-${index}`} nodes={html} className='markdown-text' />)
|
||||
currentTokens = []
|
||||
}
|
||||
|
||||
// Add code block
|
||||
result.push(
|
||||
<View key={`cb-${index}`} className='code-block-wrapper'>
|
||||
<CodeBlock
|
||||
code={token.text}
|
||||
language={token.lang}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
} else {
|
||||
currentTokens.push(token)
|
||||
}
|
||||
})
|
||||
|
||||
// Flush remaining tokens
|
||||
if (currentTokens.length > 0) {
|
||||
(currentTokens as any).links = (tokens as any).links
|
||||
const html = marked.parser(currentTokens as any, { renderer, breaks: true })
|
||||
result.push(<RichText key={`rt-end`} nodes={html} className='markdown-text' />)
|
||||
}
|
||||
|
||||
return result
|
||||
}, [content, themeColor])
|
||||
|
||||
return <View className='markdown-reader'>{elements}</View>
|
||||
}
|
||||
|
||||
export default MarkdownReader
|
||||
Reference in New Issue
Block a user