mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-24 11:08:16 +08:00
164 lines
4.2 KiB
TypeScript
164 lines
4.2 KiB
TypeScript
import type { Markdown } from 'vuepress/markdown'
|
|
import type { TableContainerOptions } from '../../shared/table.js'
|
|
import { encodeData } from '@vuepress/helper'
|
|
import { stringifyAttrs } from '../utils/stringifyAttrs.js'
|
|
import { createContainerSyntaxPlugin } from './createContainer.js'
|
|
|
|
/**
|
|
* Table container attributes
|
|
*
|
|
* 表格容器属性
|
|
*/
|
|
export interface TableContainerAttrs extends TableContainerOptions {
|
|
/**
|
|
* Table title
|
|
*
|
|
* 表格标题
|
|
*/
|
|
title?: string
|
|
|
|
/**
|
|
* Highlighted rows
|
|
*
|
|
* 表格高亮的行
|
|
*
|
|
* @example hl-rows="warning:1,2,3;error:4,5,6"
|
|
*/
|
|
hlRows?: string
|
|
/**
|
|
* Highlighted columns
|
|
*
|
|
* 表格高亮的列
|
|
*
|
|
* @example hl-cols="warning:1;error:2,3"
|
|
*/
|
|
hlCols?: string
|
|
|
|
/**
|
|
* Highlighted cells
|
|
*
|
|
* 表格高亮的单元格
|
|
*
|
|
* @example hl-cells="warning:(1,2)(2,3);error:(3,4)(4,5)"
|
|
*/
|
|
hlCells?: string
|
|
}
|
|
|
|
/**
|
|
* Table plugin - Wrap table with container for enhanced features
|
|
*
|
|
* 表格插件 - 通过容器语法将表格包裹起来,为表格提供增强功能
|
|
*
|
|
* @example
|
|
* ```md
|
|
* ::: table title="表格标题" max-content copy align="center" hl-rows="warning:1,2,3;error:4,5,6" hl-cols="warning:1;error:2,3" hl-cells="warning:(1,2)(2,3);"
|
|
*
|
|
* | xx | xx | xx |
|
|
* | -- | -- | -- |
|
|
* | xx | xx | xx |
|
|
* :::
|
|
* ```
|
|
*
|
|
* @param md - Markdown instance / Markdown 实例
|
|
* @param options - Table container options / 表格容器选项
|
|
*/
|
|
export function tablePlugin(md: Markdown, options: TableContainerOptions = {}): void {
|
|
createContainerSyntaxPlugin(md, 'table', (tokens, index, opt, env) => {
|
|
const { hlCols = '', hlRows = '', hlCells = '', ...meta } = tokens[index].meta as TableContainerAttrs
|
|
const props = { copy: true, maxContent: false, fullWidth: false, ...options, ...meta } as TableContainerAttrs & { markdown?: string }
|
|
const content = tokens[index].content
|
|
|
|
if (props.copy) {
|
|
props.copy = props.copy === true ? 'all' : props.copy
|
|
|
|
if (props.copy === 'all' || props.copy === 'md') {
|
|
props.markdown = encodeData(content.trim())
|
|
}
|
|
}
|
|
|
|
if (!hlCols && !hlRows && !hlCells) {
|
|
return `<VPTable ${stringifyAttrs(props)}>${md.render(content, env)}</VPTable>`
|
|
}
|
|
|
|
const rows = parseHl(hlRows)
|
|
const cols = parseHl(hlCols)
|
|
const cells = parseHlCells(hlCells)
|
|
|
|
const tableTokens = md.parse(content, env)
|
|
let isTable = false
|
|
let colIndex = 0
|
|
let rowIndex = 0
|
|
for (const token of tableTokens) {
|
|
if (token.type === 'table_open')
|
|
isTable = true
|
|
if (token.type === 'table_close')
|
|
isTable = false
|
|
if (!isTable)
|
|
continue
|
|
|
|
// row
|
|
if (token.type === 'tr_open') {
|
|
rowIndex++
|
|
colIndex = 0
|
|
}
|
|
// cell (rowIndex, colIndex)
|
|
if (token.type === 'th_open' || token.type === 'td_open') {
|
|
colIndex++
|
|
const classes = cells[rowIndex]?.[colIndex] || rows[rowIndex] || cols[colIndex]
|
|
if (classes)
|
|
token.attrJoin('class', classes)
|
|
}
|
|
}
|
|
|
|
return `<VPTable ${stringifyAttrs(props)}>${md.renderer.render(tableTokens, opt, env)}</VPTable>`
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Parse highlight string
|
|
*
|
|
* 解析高亮字符串
|
|
*
|
|
* @param hl - Highlight string / 高亮字符串
|
|
* @returns Parsed highlight map / 解析后的高亮映射
|
|
*/
|
|
function parseHl(hl: string) {
|
|
const res: Record<number, string> = {}
|
|
if (!hl)
|
|
return res
|
|
|
|
hl
|
|
.split(';')
|
|
.forEach((item) => {
|
|
const [key, value = '1'] = item.split(':')
|
|
String(value).split(',').forEach(v => res[v.trim()] = key.trim())
|
|
})
|
|
return res
|
|
}
|
|
|
|
/**
|
|
* Parse highlight cells string
|
|
*
|
|
* 解析高亮单元格字符串
|
|
*
|
|
* @param hl - Highlight cells string / 高亮单元格字符串
|
|
* @returns Parsed highlight cells map / 解析后的高亮单元格映射
|
|
*/
|
|
function parseHlCells(hl: string) {
|
|
const res: Record<string, Record<number, string>> = {}
|
|
if (!hl)
|
|
return res
|
|
|
|
hl
|
|
.split(';')
|
|
.forEach((item) => {
|
|
const [key, value = ''] = item.split(':')
|
|
value.trim().replace(/\s*\((\d+)\s*,\s*(\d+)\)\s*/g, (_, row, col) => {
|
|
res[row] ??= {}
|
|
res[row][col] = key.trim()
|
|
return ''
|
|
})
|
|
})
|
|
return res
|
|
}
|