class TiptapService class << self def to_html(node, tags) return '' if node.nil? children(node[:content], tags) end private def children(content, tags) content.map { node_to_html(_1, tags) }.join end def node_to_html(node, tags) case node in type: 'header', content: "<header>#{children(content, tags)}</header>" in type: 'footer', content:, **rest "<footer#{text_align(rest[:attrs])}>#{children(content, tags)}</footer>" in type: 'headerColumn', content:, **rest "<div#{text_align(rest[:attrs])} class=\"column\">#{children(content, tags)}</div>" in type: 'paragraph', content:, **rest "<p#{text_align(rest[:attrs])}>#{children(content, tags)}</p>" in type: 'title', content:, **rest "<h1#{text_align(rest[:attrs])}>#{children(content, tags)}</h1>" in type: 'heading', attrs: { level:, **attrs }, content: "<h#{level}#{text_align(attrs)}>#{children(content, tags)}</h#{level}>" in type: 'bulletList', content: "<ul>#{children(content, tags)}</ul>" in type: 'orderedList', content: "<ol>#{children(content, tags)}</ol>" in type: 'listItem', content: "<li>#{children(content, tags)}</li>" 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 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' "<strong>#{text}</strong>" in type: 'italic' "<em>#{text}</em>" in type: 'underline' "<u>#{text}</u>" in type: 'strike' "<s>#{text}</s>" in type: 'highlight' "<mark>#{text}</mark>" end end end end end