feat(tiptap): detect first body paragraph for spacing, handle empty nodes

This commit is contained in:
Colin Darie 2023-12-20 22:10:51 +01:00
parent eaf4e24a55
commit 16cdff8425
No known key found for this signature in database
GPG key ID: 8C76CADD40253590
3 changed files with 99 additions and 97 deletions

View file

@ -4,7 +4,7 @@ module Administrateurs
def show def show
json_body = @attestation_template.json_body&.deep_symbolize_keys json_body = @attestation_template.json_body&.deep_symbolize_keys
@body = TiptapService.to_html(json_body, {}) @body = TiptapService.new.to_html(json_body, {})
respond_to do |format| respond_to do |format|
format.html do format.html do

View file

@ -1,37 +1,45 @@
class TiptapService class TiptapService
class << self
def to_html(node, tags) def to_html(node, tags)
return '' if node.nil? return '' if node.nil?
children(node[:content], tags) children(node[:content], tags, 0)
end end
private private
def children(content, tags) def initialize
content.map { node_to_html(_1, tags) }.join @body_started = false
end
def children(content, tags, level)
content.map { node_to_html(_1, tags, level) }.join
end
def node_to_html(node, tags, level)
if level == 0 && !@body_started && node[:type] == 'paragraph' && node.key?(:content)
@body_started = true
body_start_mark = " class=\"body-start\""
end end
def node_to_html(node, tags)
case node case node
in type: 'header', content: in type: 'header', content:
"<header>#{children(content, tags)}</header>" "<header>#{children(content, tags, level + 1)}</header>"
in type: 'footer', content:, **rest in type: 'footer', content:, **rest
"<footer#{text_align(rest[:attrs])}>#{children(content, tags)}</footer>" "<footer#{text_align(rest[:attrs])}>#{children(content, tags, level + 1)}</footer>"
in type: 'headerColumn', content:, **rest in type: 'headerColumn', content:, **rest
"<div#{text_align(rest[:attrs])} class=\"column\">#{children(content, tags)}</div>" "<div#{text_align(rest[:attrs])}>#{children(content, tags, level + 1)}</div>"
in type: 'paragraph', content:, **rest in type: 'paragraph', content:, **rest
"<p#{text_align(rest[:attrs])}>#{children(content, tags)}</p>" "<p#{body_start_mark}#{text_align(rest[:attrs])}>#{children(content, tags, level + 1)}</p>"
in type: 'title', content:, **rest in type: 'title', content:, **rest
"<h1#{text_align(rest[:attrs])}>#{children(content, tags)}</h1>" "<h1#{text_align(rest[:attrs])}>#{children(content, tags, level + 1)}</h1>"
in type: 'heading', attrs: { level:, **attrs }, content: in type: 'heading', attrs: { level: hlevel, **attrs }, content:
"<h#{level}#{text_align(attrs)}>#{children(content, tags)}</h#{level}>" "<h#{hlevel}#{text_align(attrs)}>#{children(content, tags, level + 1)}</h#{hlevel}>"
in type: 'bulletList', content: in type: 'bulletList', content:
"<ul>#{children(content, tags)}</ul>" "<ul>#{children(content, tags, level + 1)}</ul>"
in type: 'orderedList', content: in type: 'orderedList', content:
"<ol>#{children(content, tags)}</ol>" "<ol>#{children(content, tags, level + 1)}</ol>"
in type: 'listItem', content: in type: 'listItem', content:
"<li>#{children(content, tags)}</li>" "<li>#{children(content, tags, level + 1)}</li>"
in type: 'text', text:, **rest in type: 'text', text:, **rest
if rest[:marks].present? if rest[:marks].present?
apply_marks(text, rest[:marks]) apply_marks(text, rest[:marks])
@ -73,5 +81,4 @@ class TiptapService
end end
end end
end end
end
end end

View file

@ -25,18 +25,28 @@ RSpec.describe TiptapService do
type: 'title' # remained empty in editor type: 'title' # remained empty in editor
}, },
{ {
type: 'paragraph', type: 'heading',
attrs: { textAlign: 'right' }, attrs: { level: 1 },
content: [ content: [{ type: 'text', text: 'Heading 1' }]
{
type: 'text',
text: 'Hello world!'
}
]
}, },
{ {
type: 'paragraph' type: 'heading',
# no content, empty line attrs: { level: 2, textAlign: 'center' },
content: [{ type: 'text', text: 'Heading 2' }]
},
{
type: 'heading',
attrs: { level: 3 },
content: [{ type: 'text', text: 'Heading 3' }]
},
{
type: 'heading',
attrs: { level: 3 } # remained empty in editor
},
{
type: 'paragraph',
attrs: { textAlign: 'right' },
content: [{ type: 'text', text: 'First paragraph' }]
}, },
{ {
type: 'paragraph', type: 'paragraph',
@ -63,23 +73,8 @@ RSpec.describe TiptapService do
] ]
}, },
{ {
type: 'heading', type: 'paragraph'
attrs: { level: 1 }, # no content, empty line
content: [{ type: 'text', text: 'Heading 1' }]
},
{
type: 'heading',
attrs: { level: 2, textAlign: 'center' },
content: [{ type: 'text', text: 'Heading 2' }]
},
{
type: 'heading',
attrs: { level: 3 },
content: [{ type: 'text', text: 'Heading 3' }]
},
{
type: 'heading',
attrs: { level: 3 } # remained empty in editor
}, },
{ {
type: 'bulletList', type: 'bulletList',
@ -157,13 +152,13 @@ RSpec.describe TiptapService do
let(:tags) { { 'name' => 'Paul' } } let(:tags) { { 'name' => 'Paul' } }
let(:html) do let(:html) do
[ [
'<header><div class="column">Left</div><div class="column">Right</div></header>', '<header><div>Left</div><div>Right</div></header>',
'<h1>Title</h1>', '<h1>Title</h1>',
'<p style="text-align: right">Hello world!</p>',
'<p><s><em>Bonjour </em></s><u><strong>Paul</strong></u> <mark>!</mark></p>',
'<h1>Heading 1</h1>', '<h1>Heading 1</h1>',
'<h2 style="text-align: center">Heading 2</h2>', '<h2 style="text-align: center">Heading 2</h2>',
'<h3>Heading 3</h3>', '<h3>Heading 3</h3>',
'<p class="body-start" style="text-align: right">First paragraph</p>',
'<p><s><em>Bonjour </em></s><u><strong>Paul</strong></u> <mark>!</mark></p>',
'<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>', '<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>',
'<ol><li><p>Item 1</p></li><li><p>Item 2</p></li></ol>', '<ol><li><p>Item 1</p></li><li><p>Item 2</p></li></ol>',
'<footer>Footer</footer>' '<footer>Footer</footer>'
@ -171,7 +166,7 @@ RSpec.describe TiptapService do
end end
it 'returns html' do it 'returns html' do
expect(described_class.to_html(json, tags)).to eq(html) expect(described_class.new.to_html(json, tags)).to eq(html)
end end
end end
end end