Skip to content

Commit

Permalink
Merge branch 'feature/table-colspan-rowspan' into setup-github-workfl…
Browse files Browse the repository at this point in the history
…ows-ci
  • Loading branch information
TheNorthMemory committed Aug 18, 2024
2 parents 7a7b6ea + 223c20e commit 2b2dcc5
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 0 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,46 @@ Output:
</table>
```

Wellformed the table's _rowspan_ and/or _colspan_ attributes, usage sample below:
```md
| A | B | C | D |
| ----------------------- | --- | --- | ---------------- |
| 1 | 11 | 111 | 1111 {rowspan=3} |
| 2 {colspan=2 rowspan=2} | 22 | 222 | 2222 |
| 3 | 33 | 333 | 3333 |

{border=1}
```

Output:
```html
<table border="1">
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>11</td>
<td>111</td>
<td rowspan="3">1111</td>
</tr>
<tr>
<td colspan="2" rowspan="2">2</td>
<td>22</td>
</tr>
<tr>
<td>3</td>
</tr>
</tbody>
</table>
```

If you need finer control, [decorate](https://github.com/rstacruz/markdown-it-decorate) might help you.

## Custom rendering
Expand Down
152 changes: 152 additions & 0 deletions patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,142 @@ module.exports = options => {
// remove <p>{.c}</p>
tokens.splice(i + 1, 3);
}
}, {
/**
* | A | B |
* | -- | -- |
* | 1 | 2 |
*
* | C | D |
* | -- | -- |
*
* only `| A | B |` sets the colsnum metadata
*/
name: 'tables thead metadata',
tests: [
{
shift: 0,
type: 'tr_close',
}, {
shift: 1,
type: 'thead_close'
}, {
shift: 2,
type: 'tbody_open'
}
],
transform: (tokens, i) => {
const tr = utils.getMatchingOpeningToken(tokens, i);
const th = tokens[i - 1];
let colsnum = 0;
let n = i;
while (--n) {
if (tokens[n] === tr) {
tokens[n - 1].meta = Object.assign({}, tokens[n + 2].meta, { colsnum });
break;
}
colsnum += (tokens[n].level === th.level && tokens[n].type === th.type) >> 0;
}
tokens[i + 2].meta = Object.assign({}, tokens[i + 2].meta, { colsnum });
}
}, {
/**
* | A | B | C | D |
* | -- | -- | -- | -- |
* | 1 | 11 | 111 | 1111 {rowspan=3} |
* | 2 {colspan=2 rowspan=2} | 22 | 222 | 2222 |
* | 3 | 33 | 333 | 3333 |
*/
name: 'tables tbody calculate',
tests: [
{
shift: 0,
type: 'tbody_close',
hidden: false
}
],
/**
* @param {number} i index of the tbody ending
*/
transform: (tokens, i) => {
/** index of the tbody beginning */
let idx = i - 2;
while (idx > 0 && 'tbody_open' !== tokens[--idx].type);

const calc = tokens[idx].meta ? tokens[idx].meta.colsnum >> 0 : 1;
if (calc < 2) { return; }

const level = tokens[i].level + 2;
for (let n = idx; n < i; n++) {
if (tokens[n].level > level) { continue; }

const token = tokens[n];
const rows = token.hidden ? 0 : token.attrGet('rowspan') >> 0;
const cols = token.hidden ? 0 : token.attrGet('colspan') >> 0;

if (rows > 1) {
let colsnum = calc - (cols > 0 ? cols : 1);
for (let k = n, num = rows; k < i, num > 1; k++) {
if ('tr_open' == tokens[k].type) {
tokens[k].meta = Object.assign({}, tokens[k].meta);
if (tokens[k].meta && tokens[k].meta.colsnum) {
colsnum -= 1;
}
tokens[k].meta.colsnum = colsnum;
num--;
}
}
}

if ('tr_open' == token.type && token.meta && token.meta.colsnum) {
const max = token.meta.colsnum;
for (let k = n, num = 0; k < i; k++) {
if ('td_open' == tokens[k].type) {
num += 1;
} else if ('tr_close' == tokens[k].type) {
break;
}
num > max && (tokens[k].hidden || hidden(tokens[k]));
}
}

if (cols > 1) {
/** @type {number[]} index of one row's children */
const one = [];
/** last index of the row's children */
let end = n + 3;
/** number of the row's children */
let num = calc;

for (let k = n; k > idx; k--) {
if ('tr_open' == tokens[k].type) {
num = tokens[k].meta && tokens[k].meta.colsnum || num;
break;
} else if ('td_open' === tokens[k].type) {
one.unshift(k);
}
}

for (let k = n; k < i; k++) {
if ('tr_close' == tokens[k].type) {
end = k;
break;
} else if ('td_open' == tokens[k].type) {
k > idx && one.push(k);
}
}

const off = one.indexOf(idx);
let real = num - off;
real = real > cols ? cols : real;
cols > real && token.attrSet('colspan', real + '');

for (let k = one.slice(num + 1 - calc - real)[0]; k < end; k++) {
tokens[k].hidden || hidden(tokens[k]);
}
}
}
}
}, {
/**
* *emphasis*{.with attrs=1}
Expand Down Expand Up @@ -361,3 +497,19 @@ module.exports = options => {
function last(arr) {
return arr.slice(-1)[0];
}

/**
* Hidden table's cells and them inline children,
* specially cast inline's content as empty
* to prevent that escapes the table's box model
* @see https://github.com/markdown-it/markdown-it/issues/639
* @param {import('.').Token} token
*/
function hidden(token) {
token.hidden = true;
token.children && token.children.forEach(t => (
t.content = '',
hidden(t),
undefined
));
}
36 changes: 36 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,42 @@ function describeTestsWithOptions(options, postText) {
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});

it(replaceDelimiters('should caculate table\'s colspan and/or rowspan', options), () => {
src = '| A | B | C | D |\n';
src += '| -- | -- | -- | -- |\n';
src += '| 1 | 11 | 111 | 1111 {rowspan=3} |\n';
src += '| 2 {colspan=2 rowspan=2} | 22 | 222 | 2222 |\n';
src += '| 3 | 33 | 333 | 3333 |\n';
src += '\n';
src += '{border=1}\n';
expected = '<table border="1">\n';
expected += '<thead>\n';
expected += '<tr>\n';
expected += '<th>A</th>\n';
expected += '<th>B</th>\n';
expected += '<th>C</th>\n';
expected += '<th>D</th>\n';
expected += '</tr>\n';
expected += '</thead>\n';
expected += '<tbody>\n';
expected += '<tr>\n';
expected += '<td>1</td>\n';
expected += '<td>11</td>\n';
expected += '<td>111</td>\n';
expected += '<td rowspan="3">1111</td>\n';
expected += '</tr>\n';
expected += '<tr>\n';
expected += '<td colspan="2" rowspan="2">2</td>\n';
expected += '<td>22</td>\n';
expected += '</tr>\n';
expected += '<tr>\n';
expected += '<td>3</td>\n';
expected += '</tr>\n';
expected += '</tbody>\n';
expected += '</table>\n';
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});

it(replaceDelimiters('should support nested lists', options), () => {
src = '- item\n';
src += ' - nested\n';
Expand Down

0 comments on commit 2b2dcc5

Please sign in to comment.