diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 2e62b3ec1f3693646f40b6895d04143698d79833..c7e164601c9d9164f5c1431c147c2b02a2c5beff 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -114,6 +114,9 @@ const config = { .end() .plugin('add-base-to-md') .use(require('./markdown/add-base-to-md'), [{ base }]) + .end() + .plugin('subtable') + .use(require('./markdown/markdown-it-subtable').markdownIt, [{ flags: ['->'] }]) } }, chainWebpack (config, isServer) { diff --git a/docs/.vuepress/markdown/markdown-it-subtable.js b/docs/.vuepress/markdown/markdown-it-subtable.js new file mode 100644 index 0000000000000000000000000000000000000000..50a0332dec9e69e57291d23feacffc549a134f5e --- /dev/null +++ b/docs/.vuepress/markdown/markdown-it-subtable.js @@ -0,0 +1,146 @@ +function normalizeMD (md) { + if (typeof md === 'string') { + return md + .replace(/(? { + if (Array.isArray(tokenIndex) && tokenIndex.length > 2) { + const tableOpenIndex = tokenIndex.shift(); + const tableCloseIndex = tokenIndex.pop(); + let deleteOffset = 0; + + tokenIndex.forEach(subtableIndexes => { + const subtableOpenIndex = subtableIndexes.shift() + deleteOffset; + const subtableColumnCount = (subtableIndexes[0] + deleteOffset - subtableOpenIndex - 2) / 3 - 1; + const subtableCloseIndex = subtableIndexes.pop() + deleteOffset + subtableColumnCount * 3; + const subtableTokens = tokens.slice(subtableOpenIndex - 1, subtableCloseIndex + 1); + + // 2 table token 新行开始时固定的 tr_open td_open 数量 + // 3 每个 inline 都会伴随一个 td_close、td_open(行最后一列 inline 会伴随 td_close tr_close) + // 1 为 flag ->(标识符) 的列,需要去掉 + let tableColumnCount = 0; + for (let i = 0; i < tableCloseIndex; i++) { + const token = tokens[i]; + if (i < subtableOpenIndex) { + if (token.type === 'th_open') { + tableColumnCount++; + } else if (token.type === 'thead_close') { + break; + } + } + } + + let subtableMD = subtableTokens + .map(token => { + if (token.type === 'inline' && !flags.includes(token.content)) { + return normalizeMD(token.content); + } else if (token.type === 'tr_close') { + return '\n'; + } + }) + .filter(Boolean); + const subtableLevel = subtableTokens[0].level; + const newTokens = md.parse(`|${subtableMD.join('|')}|`); + newTokens.forEach(token => { + token.content = ''; // 和 children 内容重复 + token.level = token.level + subtableLevel; + }); + newTokens[0].attrJoin('style', 'overflow-x: visible;margin: auto;'); + const childrenTableTokenIndex = subtableOpenIndex - 2; + const subtablePrevTrOpenIndex = childrenTableTokenIndex - tableColumnCount * 3 - 2; + tokens[tableOpenIndex].attrJoin('class', 'have-children-table'); // table + tokens[subtablePrevTrOpenIndex].attrJoin('class', 'have-children-tr'); // table tr_open + + // table td_open + const haveChildrenTrTdToken = tokens[subtablePrevTrOpenIndex + 1]; + haveChildrenTrTdToken.attrJoin('class', 'have-children-tr-td'); + haveChildrenTrTdToken.attrJoin('style', ';text-wrap: nowrap'); + + tokens[childrenTableTokenIndex].attrJoin('class', 'children-table'); // subtable tr_open + tokens[childrenTableTokenIndex].attrJoin('hidden', ''); // subtable tr_open + + tokens[subtableOpenIndex - 1].attrJoin('colspan', tableColumnCount); // subtable td_open + const deleteCount = subtableCloseIndex - subtableOpenIndex + 1; + tokens.splice(subtableOpenIndex, deleteCount, ...newTokens); + deleteOffset = deleteOffset + newTokens.length - deleteCount; + }); + } + }); + } +} + +function process(md, tokens, flags = ['->']) { + const subtableMinTokenCount = 3; + if ( + Array.isArray(tokens) && + tokens.length && + tokens.some(token => token.content.includes('->') && token.type === 'inline') + ) { + const tableOpenTokenIndex = tokens.findIndex(token => token.type === 'table_open'); + if (tableOpenTokenIndex > -1) { + /** + * [ + * table_open index, + * [] // subtable indexes, + * table_close index + * ] + */ + let tableTokensIndexes = [[]]; + let tableIndex = 0; + let subtableIndex = 1; + tokens.forEach((token, index) => { + if (token.type === 'table_open' && tableTokensIndexes[tableIndex].length === 0) { + tableTokensIndexes[tableIndex].push(index); + } + if (tableTokensIndexes[tableIndex] && typeof tableTokensIndexes[tableIndex][0] !== 'undefined') { + if (token.type === 'inline') { + if (flags.find(flag => new RegExp(`^\\s*${flag}\\s*$`).test(token.content))) { + ( + tableTokensIndexes[tableIndex][subtableIndex] || (tableTokensIndexes[tableIndex][subtableIndex] = []) + ).push(index); + } else if (tokens[index - 2].type === 'tr_open') { + tableTokensIndexes[tableIndex][++subtableIndex] = []; + } + } + } + if (token.type === 'table_close') { + subtableIndex = 1; + tableTokensIndexes[tableIndex].push(index); + tableTokensIndexes[++tableIndex] = []; + } + }); + tableTokensIndexes.forEach((subtableTokensIndex, index) => { + tableTokensIndexes[index] = subtableTokensIndex.filter( + i => typeof i === 'number' || (Array.isArray(i) && i.length >= subtableMinTokenCount) + ); + }); + tableTokensIndexes = tableTokensIndexes.filter(i => i.length); + if (tableTokensIndexes.length) { + resolveSubtable(md, tokens, tableTokensIndexes, flags); + } + } + } +} + +module.exports = { + process, + markdownIt: function subTablePlugin(md, { flags = ['->'] } = {}) { + md.core.ruler.after( + 'inline', + 'subtable', + (state, startLine, endLine, silent) => { + process(md, state.tokens, flags); + }, + { + alt: ['paragraph', 'reference'], + } + ); + }, +}; diff --git a/package.json b/package.json index 0cef209cad4cc4154d47d1d01d1a688742979547..31435e04f10317025f8dcab22c4d44d670aadb93 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "markdown-it-attrs": "^4.1.6", "markdown-it-raw-table": "^1.0.0", "markdown-it-task-lists": "^2.1.1", - "vuepress-theme-uni-app-test": "^1.4.5" + "vuepress-theme-uni-app-test": "^1.4.7" }, "dependencies": { "@docsearch/js": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index eaceae92d8929e385a52720de64d48df32633317..5d78e6a36f3bbbf6233808406fad95aadf5e7d2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9288,10 +9288,10 @@ vuepress-plugin-zooming@^1.1.8: dependencies: zooming "^2.1.1" -vuepress-theme-uni-app-test@^1.4.5: - version "1.4.5" - resolved "https://registry.npmmirror.com/vuepress-theme-uni-app-test/-/vuepress-theme-uni-app-test-1.4.5.tgz#c996fcf2baef6e6610e065671d6f0d29328ce97f" - integrity sha512-Zo8T2lH6d3TCf9Dghkz0NrzWbdOB0czisdbFqnB0P4H9K/qkM7SBDJCxeF9tVvS+p4GB49jwHAH24CTfDKYY9Q== +vuepress-theme-uni-app-test@^1.4.7: + version "1.4.7" + resolved "https://registry.npmmirror.com/vuepress-theme-uni-app-test/-/vuepress-theme-uni-app-test-1.4.7.tgz#566be747f080f551a0f65ac306ccc09dab2ab623" + integrity sha512-O+4FGNetUqEHBpz/EE7KJlwNP3LrU2iz7W1WEq2vHnowPyOxwB2DiSA1aHaHhX1zDAG4n/HILQXhjH48r4uGhw== dependencies: "@vuepress/plugin-back-to-top" "^1.9.5" "@vuepress/theme-default" "^1.8.2"