diff --git a/docs/notes/theme/guide/markdown/table.md b/docs/notes/theme/guide/markdown/table.md index a0ec9aa3..320dfab6 100644 --- a/docs/notes/theme/guide/markdown/table.md +++ b/docs/notes/theme/guide/markdown/table.md @@ -78,11 +78,62 @@ export default defineUserConfig({ ::: field name="maxContent" type="boolean" optional default="false" 行内元素不再自动换行,超出容器宽度时表格显示滚动条 +::: + +::: field name="hl-rows" type="string" optional +配置表格中的行高亮。 + +值使用 `type:row1,row2` 的格式,可以使用 `;` 组合多个 type row。 + +示例: + +- `hl-rows="warning:1"`: 将第一行设置为 warning 颜色 +- `hl-rows="danger:1,2"`: 将第一行和第二行设置为 danger 颜色 +- `hl-rows="warning:1,2;danger:3,4"`: 将第一行和第二行设置为 warning 颜色,第三行和第四行设置为 danger 颜色 + +`type` 内置支持:`tip/note/info/success/warning/danger/caution/important`。 + +`row` 从 1 开始计数。 +::: + +::: field name="hl-cols" type="string" optional +配置表格中的列高亮。 + +值使用 `type:col1,col2` 的格式,可以使用 `;` 组合多个 type col。 + +示例: + +- `hl-cols="warning:1"`: 将第一列设置为 warning 颜色 +- `hl-cols="danger:1,2"`: 将第一列和第二列设置为 danger 颜色 +- `hl-cols="warning:1,2;danger:3,4"`: 将第一列和第二列设置为 warning 颜色,第三列和第四列设置为 danger 颜色 + +`type` 内置支持:`tip/note/info/success/warning/danger/caution/important`。 + +`col` 从 1 开始计数。 +::: + +::: field name="hl-cells" type="string" optional +配置表格中的单元格高亮。 + +值使用 `type:(row,col)` 的格式,可以使用 `;` 组合多个 type cell。 + +示例: + +- `hl-cells="warning:(1,1)"`: 将第一行第一列设置为 warning 颜色 +- `hl-cells="danger:(1,1)(2,2)"`: 将第一行第一列,第二行第二列设置为 danger 颜色 +- `hl-cells="warning:(1,1)(2,2);danger:(3,3)(4,4)"`: 将第一行第一列,第二行第二列设置为 warning 颜色,第三行第三列,第四行第四列设置为 danger 颜色 + +`type` 内置支持:`tip/note/info/success/warning/danger/caution/important`。 + +`row` 从 1 开始计数,`col` 从 1 开始计数。 + ::: :::: ## 示例 +### 表格标题 + **输入:** ```md @@ -105,6 +156,8 @@ export default defineUserConfig({ ::: +### 表格对齐 + **输入:** ```md @@ -127,6 +180,8 @@ export default defineUserConfig({ ::: +### 表格内容宽度 + **输入:** ```md @@ -149,3 +204,131 @@ export default defineUserConfig({ | 2 | Short text | ✅ Completed | ::: + +### 表格行高亮 + +**输入:** + +```md +::: table title="这是表格标题" hl-rows="tip:1;warning:2;important:3,4" +| row1 | row1 | row1 | +| ---- | ---- | ---- | +| row2 | row2 | row2 | +| row3 | row3 | row3 | +| row4 | row4 | row4 | +::: +``` + +**输出:** + +::: table title="这是表格标题" hl-rows="tip:1;warning:2;important:3,4" + +| row1 | row1 | row1 | +| ---- | ---- | ---- | +| row2 | row2 | row2 | +| row3 | row3 | row3 | +| row4 | row4 | row4 | + +::: + +### 表格列高亮 + +**输入:** + +```md +::: table title="这是表格标题" hl-cols="success:1;warning:2;danger:3,4" +| col1 | col2 | col3 | col4 | +| ---- | ---- | ---- | ---- | +| col1 | col2 | col3 | col4 | +| col1 | col2 | col3 | col4 | +| col1 | col2 | col3 | col4 | +::: +``` + +**输出:** + +::: table title="这是表格标题" hl-cols="success:1;warning:2;danger:3,4" + +| col1 | col2 | col3 | col4 | +| ---- | ---- | ---- | ---- | +| col1 | col2 | col3 | col4 | +| col1 | col2 | col3 | col4 | +| col1 | col2 | col3 | col4 | + +::: + +### 表格单元格高亮 + +**输入:** + +```md +::: table title="这是表格标题" hl-cells="danger:(1,1)(2,2);success:(3,3)(4,4);warning:(1,4)(2,3);important:(3,2)(4,1)" +| (1,1) | (1,2) | (1,3) | (1,4) | +| ----- | ----- | ----- | ----- | +| (2,1) | (2,2) | (2,3) | (2,4) | +| (3,1) | (3,2) | (3,3) | (3,4) | +| (4,1) | (4,2) | (4,3) | (4,4) | +::: +``` + +**输出:** + +::: table title="这是表格标题" hl-cells="danger:(1,1)(2,2);success:(3,3)(4,4);warning:(1,4)(2,3);important:(3,2)(4,1)" + +| (1,1) | (1,2) | (1,3) | (1,4) | +| ----- | ----- | ----- | ----- | +| (2,1) | (2,2) | (2,3) | (2,4) | +| (3,1) | (3,2) | (3,3) | (3,4) | +| (4,1) | (4,2) | (4,3) | (4,4) | + +::: + +## 自定义高亮类型 + +在 [自定义 CSS 样式](../custom/style.md) 中,通过以下格式可以自定义高亮类型: + +```css +.vp-table table th.type, +.vp-table table td.type { + color: #000; + background-color: #fff; +} +``` + +比如,添加一个 `blue` 的高亮类型: + +```css +.vp-table table th.blue, +.vp-table table td.blue { + color: #4a7cb9; + background-color: #a3c6e5; +} +``` + + + +然后就可以在表格中使用: + +```md +::: table hl-rows="blue:1" hl-cols="blue:1" hl-cells="blue:(3,3)" +| Header 1 | Header 2 | Header 3 | +| -------- | -------- | -------- | +| Cell 1 | Cell 2 | Cell 3 | +| Cell 4 | Cell 5 | Cell 6 | +::: +``` + +::: table hl-rows="blue:1" hl-cols="blue:1" hl-cells="blue:(3,3)" + +| Header 1 | Header 2 | Header 3 | +| -------- | -------- | -------- | +| Cell 1 | Cell 2 | Cell 3 | +| Cell 4 | Cell 5 | Cell 6 | + +::: diff --git a/plugins/plugin-md-power/src/client/components/VPTable.vue b/plugins/plugin-md-power/src/client/components/VPTable.vue index 714dada6..e0d33898 100644 --- a/plugins/plugin-md-power/src/client/components/VPTable.vue +++ b/plugins/plugin-md-power/src/client/components/VPTable.vue @@ -143,4 +143,45 @@ function onCopy(type: 'html' | 'md') { .vp-table .table-content .max-content table { width: max-content; } + +/* ----- Highlight --------- */ +.vp-table table th.tip, +.vp-table table td.tip, +.vp-table table th.note, +.vp-table table td.note { + color: var(--vp-c-tip-1); + background-color: var(--vp-c-tip-soft); +} + +.vp-table table th.info, +.vp-table table td.info { + color: var(--vp-c-default-1); + background-color: var(--vp-c-default-soft); +} + +.vp-table table th.warning, +.vp-table table td.warning { + color: var(--vp-c-warning-1); + background-color: var(--vp-c-warning-soft); +} + +.vp-table table th.danger, +.vp-table table td.danger, +.vp-table table th.caution, +.vp-table table td.caution { + color: var(--vp-c-danger-1); + background-color: var(--vp-c-danger-soft); +} + +.vp-table table th.success, +.vp-table table td.success { + color: var(--vp-c-success-1); + background-color: var(--vp-c-success-soft); +} + +.vp-table table th.important, +.vp-table table td.important { + color: var(--vp-c-important-1); + background-color: var(--vp-c-important-soft); +} diff --git a/plugins/plugin-md-power/src/node/container/table.ts b/plugins/plugin-md-power/src/node/container/table.ts index 4fd93586..af6f71cc 100644 --- a/plugins/plugin-md-power/src/node/container/table.ts +++ b/plugins/plugin-md-power/src/node/container/table.ts @@ -5,25 +5,125 @@ import { stringifyAttrs } from '../utils/stringifyAttrs.js' import { createContainerSyntaxPlugin } from './createContainer.js' export interface TableContainerAttrs extends TableContainerOptions { + /** + * 表格标题 + */ title?: string + + /** + * 表格高亮的行 + * + * @example hl-rows="warning:1,2,3;error:4,5,6" + */ + hlRows?: string + /** + * 表格高亮的列 + * + * @example hl-cols="warning:1;error:2,3" + */ + hlCols?: string + + /** + * 表格高亮的单元格 + * + * @example hl-cells="warning:(1,2)(2,3);error:(3,4)(4,5)" + */ + hlCells?: string } /** * 在不破坏表格语法的前提下,通过容器语法将表格包裹起来,为表格提供增强功能 + * + * @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 | + * ::: + * ``` */ export function tablePlugin(md: Markdown, options: TableContainerOptions = {}): void { - createContainerSyntaxPlugin(md, 'table', (tokens, index, _, env) => { - const meta = { copy: true, maxContent: false, ...options, ...tokens[index].meta } as TableContainerAttrs & { markdown?: string } + createContainerSyntaxPlugin(md, 'table', (tokens, index, opt, env) => { + const { hlCols = '', hlRows = '', hlCells = '', ...meta } = tokens[index].meta as TableContainerAttrs + const props = { copy: true, maxContent: false, ...options, ...meta } as TableContainerAttrs & { markdown?: string } const content = tokens[index].content - if (meta.copy) { - meta.copy = meta.copy === true ? 'all' : meta.copy + if (props.copy) { + props.copy = props.copy === true ? 'all' : props.copy - if (meta.copy === 'all' || meta.copy === 'md') { - meta.markdown = encodeData(content.trim()) + if (props.copy === 'all' || props.copy === 'md') { + props.markdown = encodeData(content.trim()) } } - return `${md.render(content, env)}` + if (!hlCols && !hlRows && !hlCells) { + return `${md.render(content, env)}` + } + + 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 `${md.renderer.render(tableTokens, opt, env)}` }) } + +function parseHl(hl: string) { + const res: Record = {} + 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 +} + +function parseHlCells(hl: string) { + const res: Record> = {} + 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 +}