Skip to content

Commit 39585ac

Browse files
Merge pull request #456 from dbolack-ab/columnWidths
Column widths
2 parents 9e56b24 + 319f4ad commit 39585ac

File tree

7 files changed

+177
-86
lines changed

7 files changed

+177
-86
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ Headers can now follow the same structure as cells, to include multiple rows, an
3737
| Cell A | Cell B | Cell C |
3838
```
3939

40+
## Column widths
41+
Column widths may be set by percentage. At least one hyphen on is required on each side of the width value, padding with spaces allowed: `|-10%-|` or `|- 10% -|`.
42+
43+
```
44+
|Column One|Column Two|Column Three|
45+
|--10%-----|-- 40% ---|:---50%-----|
46+
```
47+
4048
# Usage
4149
<!-- Show most examples of how to use this extension -->
4250

lib/index.cjs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function index(endRegex = []) {
1010
tokenizer(src, tokens) {
1111
// const regex = this.tokenizer.rules.block.table;
1212
let regexString = '^ *([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' // Header
13-
+ ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' // Align
13+
+ ' {0,3}(?:\\| *)?(:?-+(?: *(?:100|[1-9][0-9]?%) *-)?-*:? *(?:\\| *:?-+(?: *(?:100|[1-9][0-9]?%) *-)?-*:? *)*)(?:\\| *)?' // Align
1414
+ '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' // Cells
1515
+ '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}'
1616
+ '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |'
@@ -23,15 +23,17 @@ function index(endRegex = []) {
2323
+ '(?: +|\\n|\\/?>)|<(?:script|pre|style|textarea|!--)endRegex).*(?:\\n|$))*)\\n*|$)'; // Cells
2424

2525
regexString = regexString.replace('endRegex', endRegex.map(str => `|(?:${str})`).join(''));
26+
const widthRegex = /(?:100|[1-9][0-9]?%)/g;
2627
const regex = new RegExp(regexString);
2728
const cap = regex.exec(src);
2829

2930
if (cap) {
3031
const item = {
3132
type: 'spanTable',
3233
header: cap[1].replace(/\n$/, '').split('\n'),
33-
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
34-
rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
34+
align: cap[2].replace(widthRegex, '').replace(/^ *|\| *$/g, '').split(/ *\| */),
35+
rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
36+
width: cap[2].replace(/:/g, '').replace(/-+| /g, '').split('|')
3537
};
3638

3739
// Get first header row to determine how many columns
@@ -107,7 +109,7 @@ function index(endRegex = []) {
107109
for (j = 0; j < row.length; j++) {
108110
cell = row[j];
109111
text = this.parser.parseInline(cell.tokens);
110-
output += getTableCell(text, cell, 'th', token.align[col]);
112+
output += getTableCell(text, cell, 'th', token.align[col], token.width[col]);
111113
col += cell.colspan;
112114
}
113115
output += '</tr>';
@@ -122,7 +124,7 @@ function index(endRegex = []) {
122124
for (j = 0; j < row.length; j++) {
123125
cell = row[j];
124126
text = this.parser.parseInline(cell.tokens);
125-
output += getTableCell(text, cell, 'td', token.align[col]);
127+
output += getTableCell(text, cell, 'td', token.align[col], token.width[col]);
126128
col += cell.colspan;
127129
}
128130
output += '</tr>';
@@ -137,14 +139,15 @@ function index(endRegex = []) {
137139
};
138140
}
139141

140-
const getTableCell = (text, cell, type, align) => {
142+
const getTableCell = (text, cell, type, align, width) => {
141143
if (!cell.rowspan) {
142144
return '';
143145
}
144146
const tag = `<${type}`
145147
+ `${cell.colspan > 1 ? ` colspan=${cell.colspan}` : ''}`
146148
+ `${cell.rowspan > 1 ? ` rowspan=${cell.rowspan}` : ''}`
147-
+ `${align ? ` align=${align}` : ''}>`;
149+
+ `${align ? ` align=${align}` : ''}`
150+
+ `${width ? ` width=${width}` : ''}>`;
148151
return `${tag + text}</${type}>\n`;
149152
};
150153

lib/index.umd.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
tokenizer(src, tokens) {
1515
// const regex = this.tokenizer.rules.block.table;
1616
let regexString = '^ *([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' // Header
17-
+ ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' // Align
17+
+ ' {0,3}(?:\\| *)?(:?-+(?: *(?:100|[1-9][0-9]?%) *-)?-*:? *(?:\\| *:?-+(?: *(?:100|[1-9][0-9]?%) *-)?-*:? *)*)(?:\\| *)?' // Align
1818
+ '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' // Cells
1919
+ '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}'
2020
+ '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |'
@@ -27,15 +27,17 @@
2727
+ '(?: +|\\n|\\/?>)|<(?:script|pre|style|textarea|!--)endRegex).*(?:\\n|$))*)\\n*|$)'; // Cells
2828

2929
regexString = regexString.replace('endRegex', endRegex.map(str => `|(?:${str})`).join(''));
30+
const widthRegex = / *(?:100|[1-9][0-9]?%) */g;
3031
const regex = new RegExp(regexString);
3132
const cap = regex.exec(src);
3233

3334
if (cap) {
3435
const item = {
3536
type: 'spanTable',
3637
header: cap[1].replace(/\n$/, '').split('\n'),
37-
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
38-
rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
38+
align: cap[2].replace(widthRegex, '').replace(/^ *|\| *$/g, '').split(/ *\| */),
39+
rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
40+
width: cap[2].replace(/:/g, '').replace(/-+| /g, '').split('|')
3941
};
4042

4143
// Get first header row to determine how many columns
@@ -111,7 +113,7 @@
111113
for (j = 0; j < row.length; j++) {
112114
cell = row[j];
113115
text = this.parser.parseInline(cell.tokens);
114-
output += getTableCell(text, cell, 'th', token.align[col]);
116+
output += getTableCell(text, cell, 'th', token.align[col], token.width[col]);
115117
col += cell.colspan;
116118
}
117119
output += '</tr>';
@@ -126,7 +128,7 @@
126128
for (j = 0; j < row.length; j++) {
127129
cell = row[j];
128130
text = this.parser.parseInline(cell.tokens);
129-
output += getTableCell(text, cell, 'td', token.align[col]);
131+
output += getTableCell(text, cell, 'td', token.align[col], token.width[col]);
130132
col += cell.colspan;
131133
}
132134
output += '</tr>';
@@ -141,14 +143,15 @@
141143
};
142144
}
143145

144-
const getTableCell = (text, cell, type, align) => {
146+
const getTableCell = (text, cell, type, align, width) => {
145147
if (!cell.rowspan) {
146148
return '';
147149
}
148150
const tag = `<${type}`
149151
+ `${cell.colspan > 1 ? ` colspan=${cell.colspan}` : ''}`
150152
+ `${cell.rowspan > 1 ? ` rowspan=${cell.rowspan}` : ''}`
151-
+ `${align ? ` align=${align}` : ''}>`;
153+
+ `${align ? ` align=${align}` : ''}`
154+
+ `${width ? ` width=${width}` : ''}>`;
152155
return `${tag + text}</${type}>\n`;
153156
};
154157

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/__snapshots__/index.test.js.snap

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,41 @@ exports[`extended-table Stops at custom terminators 1`] = `
4747
</tr></tbody></table><p>aaaa</p>
4848
"
4949
`;
50+
51+
exports[`extended-table Works with combined widths and alignment 1`] = `
52+
"<table><thead><tr><th align=left width=10%>Header A</th>
53+
<th align=center width=20%>Header B</th>
54+
<th align=right width=50%>Header C</th>
55+
</tr></thead><tbody><tr><td align=left width=10%>Cell A</td>
56+
<td align=center width=20%>Cell B</td>
57+
<td align=right width=50%>Cell C</td>
58+
</tr></tbody></table>"
59+
`;
60+
61+
exports[`extended-table Works with minimal delimiter rows 1`] = `
62+
"<table><thead><tr><th>Header A</th>
63+
<th align=left>Header B</th>
64+
<th align=right>Header C</th>
65+
<th align=center>Header D</th>
66+
</tr></thead><tbody><tr><td>Cell A</td>
67+
<td align=left>Cell B</td>
68+
<td align=right>Cell C</td>
69+
<td align=center>Cell D</td>
70+
</tr></tbody></table>"
71+
`;
72+
73+
exports[`extended-table Works with mix of widths and not 1`] = `
74+
"<table><thead><tr><th width=10%>Header A</th>
75+
<th>Header B</th>
76+
<th width=50%>Header C</th>
77+
</tr></thead><tbody><tr><td width=10%>Cell A</td>
78+
<td>Cell B</td>
79+
<td width=50%>Cell C</td>
80+
</tr></tbody></table>"
81+
`;
82+
83+
exports[`extended-table Works with percentage widths 1`] = `
84+
"<table><thead><tr><th width=10%>Header A</th>
85+
</tr></thead><tbody><tr><td width=10%>Cell A</td>
86+
</tr></tbody></table>"
87+
`;

spec/index.test.js

Lines changed: 100 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,100 @@
1-
import { marked } from 'marked';
2-
import extendedTable from '../src/index.js';
3-
4-
function trimLines(s) {
5-
return s.split('\n').map(l => l.trim()).join('\n');
6-
}
7-
8-
describe('extended-table', () => {
9-
beforeEach(() => {
10-
marked.setOptions(marked.getDefaults());
11-
});
12-
13-
test('Column Spanning', () => {
14-
marked.use(extendedTable());
15-
expect(marked(trimLines(`
16-
| H1 | H2 | H3 |
17-
|---------|---------|---------|
18-
| This cell spans 3 columns |||
19-
`))).toMatchSnapshot();
20-
});
21-
22-
test('Row Spanning', () => {
23-
marked.use(extendedTable());
24-
expect(marked(trimLines(`
25-
| H1 | H2 |
26-
|--------------|---------|
27-
| This cell | Cell A |
28-
| spans three ^| Cell B |
29-
| rows ^| Cell C |
30-
`))).toMatchSnapshot();
31-
});
32-
33-
test('Multi-row headers', () => {
34-
marked.use(extendedTable());
35-
expect(marked(trimLines(`
36-
| This header spans two || Header A |
37-
| columns *and* two rows ^|| Header B |
38-
|-------------|------------|----------|
39-
| Cell A | Cell B | Cell C |
40-
`))).toMatchSnapshot();
41-
});
42-
43-
test('Stops at custom terminators', () => {
44-
marked.use(extendedTable(['aaaa']));
45-
expect(marked(trimLines(`
46-
| Header A | Header B |
47-
|----------|----------|
48-
| Cell A | Cell B |
49-
aaaa
50-
`))).toMatchSnapshot();
51-
});
52-
53-
test('Stops at custom multiline terminators', () => {
54-
marked.use(extendedTable(['aaaa\nbbbb']));
55-
expect(marked(trimLines(`
56-
| Header A | Header B |
57-
|----------|----------|
58-
| Cell A | Cell B |
59-
aaaa
60-
bbbb
61-
cccc
62-
`))).toMatchSnapshot();
63-
});
64-
});
1+
import { marked } from 'marked';
2+
import extendedTable from '../src/index.js';
3+
4+
function trimLines(s) {
5+
return s.split('\n').map(l => l.trim()).join('\n');
6+
}
7+
8+
describe('extended-table', () => {
9+
beforeEach(() => {
10+
marked.setOptions(marked.getDefaults());
11+
});
12+
13+
test('Column Spanning', () => {
14+
marked.use(extendedTable());
15+
expect(marked(trimLines(`
16+
| H1 | H2 | H3 |
17+
|---------|---------|---------|
18+
| This cell spans 3 columns |||
19+
`))).toMatchSnapshot();
20+
});
21+
22+
test('Row Spanning', () => {
23+
marked.use(extendedTable());
24+
expect(marked(trimLines(`
25+
| H1 | H2 |
26+
|--------------|---------|
27+
| This cell | Cell A |
28+
| spans three ^| Cell B |
29+
| rows ^| Cell C |
30+
`))).toMatchSnapshot();
31+
});
32+
33+
test('Multi-row headers', () => {
34+
marked.use(extendedTable());
35+
expect(marked(trimLines(`
36+
| This header spans two || Header A |
37+
| columns *and* two rows ^|| Header B |
38+
|-------------|------------|----------|
39+
| Cell A | Cell B | Cell C |
40+
`))).toMatchSnapshot();
41+
});
42+
43+
test('Stops at custom terminators', () => {
44+
marked.use(extendedTable(['aaaa']));
45+
expect(marked(trimLines(`
46+
| Header A | Header B |
47+
|----------|----------|
48+
| Cell A | Cell B |
49+
aaaa
50+
`))).toMatchSnapshot();
51+
});
52+
53+
test('Stops at custom multiline terminators', () => {
54+
marked.use(extendedTable(['aaaa\nbbbb']));
55+
expect(marked(trimLines(`
56+
| Header A | Header B |
57+
|----------|----------|
58+
| Cell A | Cell B |
59+
aaaa
60+
bbbb
61+
cccc
62+
`))).toMatchSnapshot();
63+
});
64+
65+
test('Works with minimal delimiter rows', () => {
66+
marked.use(extendedTable());
67+
expect(marked(trimLines(`
68+
| Header A | Header B | Header C | Header D |
69+
|-|:-|-:|:-:|
70+
| Cell A | Cell B | Cell C | Cell D |
71+
`))).toMatchSnapshot();
72+
});
73+
74+
test('Works with percentage widths', () => {
75+
marked.use(extendedTable());
76+
expect(marked(trimLines(`
77+
| Header A |
78+
|---10%----|
79+
| Cell A |
80+
`))).toMatchSnapshot();
81+
});
82+
83+
test('Works with mix of widths and not', () => {
84+
marked.use(extendedTable());
85+
expect(marked(trimLines(`
86+
| Header A | Header B | Header C |
87+
|---10%----|----------|--- 50% --|
88+
| Cell A | Cell B | Cell C |
89+
`))).toMatchSnapshot();
90+
});
91+
92+
test('Works with combined widths and alignment', () => {
93+
marked.use(extendedTable());
94+
expect(marked(trimLines(`
95+
| Header A | Header B | Header C |
96+
|:---10%---|:-- 20% -:|---50%---:|
97+
| Cell A | Cell B | Cell C |
98+
`))).toMatchSnapshot();
99+
});
100+
});

0 commit comments

Comments
 (0)