class TiptapService def to_html(node, tags) return '' if node.nil? children(node[:content], tags, 0) end private def initialize @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 case node in type: 'header', content: "
#{children(content, tags, level + 1)}
" in type: 'footer', content:, **rest "#{children(content, tags, level + 1)}" in type: 'headerColumn', content:, **rest "#{children(content, tags, level + 1)}" in type: 'paragraph', content:, **rest "#{children(content, tags, level + 1)}

" in type: 'title', content:, **rest "#{children(content, tags, level + 1)}" in type: 'heading', attrs: { level: hlevel, **attrs }, content: "#{children(content, tags, level + 1)}" in type: 'bulletList', content: "
    #{children(content, tags, level + 1)}
" in type: 'orderedList', content: "
    #{children(content, tags, level + 1)}
" in type: 'listItem', content: "
  • #{children(content, tags, level + 1)}
  • " in type: 'text', text:, **rest if rest[:marks].present? apply_marks(text, rest[:marks]) else text end in type: 'mention', attrs: { id: }, **rest if rest[:marks].present? apply_marks(tags[id], rest[:marks]) else tags[id] end in { type: type } if ["paragraph", "title", "heading"].include?(type) && !node.key?(:content) # noop end end def text_align(attrs) if attrs.present? && attrs[:textAlign].present? " style=\"text-align: #{attrs[:textAlign]}\"" else "" end end def apply_marks(text, marks) marks.reduce(text) do |text, mark| case mark in type: 'bold' "#{text}" in type: 'italic' "#{text}" in type: 'underline' "#{text}" in type: 'strike' "#{text}" in type: 'highlight' "#{text}" end end end end