Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop: Make table in HTML format horizontally scrollable #11198

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table><tbody><tr><td>Left side of the main table</td><td><b>Nested Table</b><table><tbody><tr><td>nested table C1</td><td>nested table C2</td></tr><tr><td>nested table</td><td>nested table</td></tr></tbody></table></td></tr></tbody></table>
<div class="joplin-table-wrapper"><table><tbody><tr><td>Left side of the main table</td><td><b>Nested Table</b><table><tbody><tr><td>nested table C1</td><td>nested table C2</td></tr><tr><td>nested table</td><td>nested table</td></tr></tbody></table></td></tr></tbody></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_blockquote.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td><blockquote><p>Finally, from so little sleeping and so much reading, his brain dried up and he went completely out of his mind.</p><p>- Miguel de Cervantes</p></blockquote></td><td>d</td></tr></tbody></table>
<div class="joplin-table-wrapper"><table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td><blockquote><p>Finally, from so little sleeping and so much reading, his brain dried up and he went completely out of his mind.</p><p>- Miguel de Cervantes</p></blockquote></td><td>d</td></tr></tbody></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_code_1.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table><thead><tr><th>Code</th><th>Description</th></tr><tr><td><pre><code>const test = "hello";</code></pre></td><td>abcd</td></tr><tr><td><pre><code>const test = "hello";</code></pre></td><td>abcd</td></tr></thead></table>
<div class="joplin-table-wrapper"><table><thead><tr><th>Code</th><th>Description</th></tr><tr><td><pre><code>const test = "hello";</code></pre></td><td>abcd</td></tr><tr><td><pre><code>const test = "hello";</code></pre></td><td>abcd</td></tr></thead></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_code_2.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table class="jop-noMdConv"><thead class="jop-noMdConv"><tr class="jop-noMdConv"><th class="jop-noMdConv">Code</th><th class="jop-noMdConv">Description</th></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcda</td></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcd</td></tr></thead></table>
<div class="joplin-table-wrapper"><table class="jop-noMdConv"><thead class="jop-noMdConv"><tr class="jop-noMdConv"><th class="jop-noMdConv">Code</th><th class="jop-noMdConv">Description</th></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcda</td></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcd</td></tr></thead></table></div>
14 changes: 7 additions & 7 deletions packages/app-cli/tests/html_to_md/table_with_code_3.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<table class="jop-noMdConv"><thead class="jop-noMdConv"><tr class="jop-noMdConv"><th class="jop-noMdConv">Code</th><th class="jop-noMdConv">Description</th></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";
<!-- -->
<div class="joplin-table-wrapper"><table class="jop-noMdConv"><thead class="jop-noMdConv"><tr class="jop-noMdConv"><th class="jop-noMdConv">Code</th><th class="jop-noMdConv">Description</th></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";

// Another line
console.log('Test...');
<!-- -->

// Blank lines
<!-- -->
<!-- -->
<!-- -->
// Should not break things.</code></pre></td><td class="jop-noMdConv">abcda</td></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcd</td></tr></thead></table>



// Should not break things.</code></pre></td><td class="jop-noMdConv">abcda</td></tr><tr class="jop-noMdConv"><td class="jop-noMdConv"><pre class="jop-noMdConv"><code class="">const test = "hello";</code></pre></td><td class="jop-noMdConv">abcd</td></tr></thead></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_heading.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td><h1>Testing</h1><p>hello</p></td><td>d</td></tr></tbody></table>
<div class="joplin-table-wrapper"><table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td><h1>Testing</h1><p>hello</p></td><td>d</td></tr></tbody></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_hr.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>One line<hr>Two line</td><td>d</td></tr></tbody></table>
<div class="joplin-table-wrapper"><table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>One line<hr>Two line</td><td>d</td></tr></tbody></table></div>
2 changes: 1 addition & 1 deletion packages/app-cli/tests/html_to_md/table_with_list.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<table border="1" style="border-collapse: collapse; width: 100%;" data-mce-selected="1"><tbody><tr><td style="width: 50.0518%;">Header 1</td><td style="width: 50.0518%;">Header 2</td></tr><tr><td style="width: 50.0518%;"><br></td><td style="width: 50.0518%;"><ul><li>Check 1</li><li>Check 2</li></ul></td></tr></tbody></table>
<div class="joplin-table-wrapper"><table border="1" style="border-collapse: collapse; width: 100%;" data-mce-selected="1"><tbody><tr><td style="width: 50.0518%;">Header 1</td><td style="width: 50.0518%;">Header 2</td></tr><tr><td style="width: 50.0518%;"><br></td><td style="width: 50.0518%;"><ul><li>Check 1</li><li>Check 2</li></ul></td></tr></tbody></table></div>
80 changes: 56 additions & 24 deletions packages/turndown-plugin-gfm/src/tables.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var rules = {}
var alignMap = { left: ':---', right: '---:', center: ':---:' };

let isCodeBlock_ = null;
let options_ = null;

// We need to cache the result of tableShouldBeSkipped() as it is expensive.
// Caching it means we went from about 9000 ms for rendering down to 90 ms.
Expand Down Expand Up @@ -72,36 +73,57 @@ rules.tableRow = {
}

rules.table = {
// Only convert tables that can result in valid Markdown
// Other tables are kept as HTML using `keep` (see below).
filter: function (node, options) {
return node.nodeName === 'TABLE' && !tableShouldBeHtml(node, options);
return node.nodeName === 'TABLE';
},

replacement: function (content, node) {
if (tableShouldBeSkipped(node)) return content;

// Ensure there are no blank lines
content = content.replace(/\n+/g, '\n')

// If table has no heading, add an empty one so as to get a valid Markdown table
var secondLine = content.trim().split('\n');
if (secondLine.length >= 2) secondLine = secondLine[1]
var secondLineIsDivider = /\| :?---/.test(secondLine);

var columnCount = tableColCount(node);
var emptyHeader = ''
if (columnCount && !secondLineIsDivider) {
emptyHeader = '|' + ' |'.repeat(columnCount) + '\n' + '|'
for (var columnIndex = 0; columnIndex < columnCount; ++columnIndex) {
emptyHeader += ' ' + getBorder(getColumnAlignment(node, columnIndex)) + ' |';
// Only convert tables that can result in valid Markdown
// Other tables are kept as HTML using `keep` (see below).
if (tableShouldBeHtml(node, options_)) {
let html = node.outerHTML;
let divParent = nodeParentDiv(node)
// Make table in HTML format horizontally scrollable by give table a div parent, so the width of the table is limited to the screen width.
// see https://github.com/laurent22/joplin/pull/10161
// test cases:
// packages/app-cli/tests/html_to_md/preserve_nested_tables.html
// packages/app-cli/tests/html_to_md/table_with_blockquote.html
// packages/app-cli/tests/html_to_md/table_with_code_1.html
// packages/app-cli/tests/html_to_md/table_with_code_2.html
// packages/app-cli/tests/html_to_md/table_with_code_3.html
// packages/app-cli/tests/html_to_md/table_with_heading.html
// packages/app-cli/tests/html_to_md/table_with_hr.html
// packages/app-cli/tests/html_to_md/table_with_list.html
if (divParent === null || !divParent.classList.contains('joplin-table-wrapper')){
return `\n\n<div class="joplin-table-wrapper">${html}</div>\n\n`;
} else {
return html
}
} else {
if (tableShouldBeSkipped(node)) return content;

// Ensure there are no blank lines
content = content.replace(/\n+/g, '\n')

// If table has no heading, add an empty one so as to get a valid Markdown table
var secondLine = content.trim().split('\n');
if (secondLine.length >= 2) secondLine = secondLine[1]
var secondLineIsDivider = /\| :?---/.test(secondLine);

var columnCount = tableColCount(node);
var emptyHeader = ''
if (columnCount && !secondLineIsDivider) {
emptyHeader = '|' + ' |'.repeat(columnCount) + '\n' + '|'
for (var columnIndex = 0; columnIndex < columnCount; ++columnIndex) {
emptyHeader += ' ' + getBorder(getColumnAlignment(node, columnIndex)) + ' |';
}
}
}

const captionContent = node.caption ? node.caption.textContent || '' : '';
const caption = captionContent ? `${captionContent}\n\n` : '';
const tableContent = `${emptyHeader}${content}`.trimStart();
return `\n\n${caption}${tableContent}\n\n`;
const captionContent = node.caption ? node.caption.textContent || '' : '';
const caption = captionContent ? `${captionContent}\n\n` : '';
const tableContent = `${emptyHeader}${content}`.trimStart();
return `\n\n${caption}${tableContent}\n\n`;
}
}
}

Expand Down Expand Up @@ -232,6 +254,15 @@ function tableShouldBeSkipped_(tableNode) {
return false;
}

function nodeParentDiv(node) {
let parent = node.parentNode;
while (parent.nodeName !== 'DIV') {
parent = parent.parentNode;
if (!parent) return null;
}
return parent;
}

function nodeParentTable(node) {
let parent = node.parentNode;
while (parent.nodeName !== 'TABLE') {
Expand Down Expand Up @@ -261,6 +292,7 @@ function tableColCount(node) {

export default function tables (turndownService) {
isCodeBlock_ = turndownService.isCodeBlock;
options_ = turndownService.options;

turndownService.keep(function (node) {
if (node.nodeName === 'TABLE' && tableShouldBeHtml(node, turndownService.options)) return true;
Expand Down