2024-04-29 00:17:15 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2023-10-20 22:50:39 +02:00
|
|
|
class TiptapService
|
2024-02-01 18:55:41 +01:00
|
|
|
# NOTE: node must be deep symbolized keys
|
2024-05-29 23:00:34 +02:00
|
|
|
def self.used_tags_and_libelle_for(node, tags = Set.new)
|
2024-01-18 12:38:46 +01:00
|
|
|
case node
|
2024-02-01 18:55:41 +01:00
|
|
|
in type: 'mention', attrs: { id:, label: }, **rest
|
|
|
|
tags << [id, label]
|
|
|
|
in { content:, **rest } if content.is_a?(Array)
|
|
|
|
content.each { used_tags_and_libelle_for(_1, tags) }
|
|
|
|
in type:, **rest
|
2024-01-18 12:38:46 +01:00
|
|
|
# noop
|
|
|
|
end
|
|
|
|
|
|
|
|
tags
|
2023-12-20 22:10:51 +01:00
|
|
|
end
|
2023-10-20 22:50:39 +02:00
|
|
|
|
2024-05-29 23:00:34 +02:00
|
|
|
def to_html(node, substitutions = {})
|
|
|
|
return '' if node.nil?
|
|
|
|
|
2024-09-04 15:04:07 +02:00
|
|
|
children(node[:content], substitutions, 0).gsub('<p></p>', '')
|
2024-05-29 23:00:34 +02:00
|
|
|
end
|
|
|
|
|
2024-07-15 23:22:04 +02:00
|
|
|
def to_texts_and_tags(node, substitutions = {})
|
2024-05-29 23:00:34 +02:00
|
|
|
return '' if node.nil?
|
|
|
|
|
2024-07-15 23:22:04 +02:00
|
|
|
children_texts_and_tags(node[:content], substitutions)
|
2024-05-29 23:00:34 +02:00
|
|
|
end
|
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
private
|
2023-10-20 22:50:39 +02:00
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
def initialize
|
|
|
|
@body_started = false
|
|
|
|
end
|
2023-10-20 22:50:39 +02:00
|
|
|
|
2024-07-15 23:22:04 +02:00
|
|
|
def children_texts_and_tags(content, substitutions)
|
|
|
|
content.map { node_to_texts_and_tags(_1, substitutions) }.join
|
2024-03-02 22:41:05 +01:00
|
|
|
end
|
|
|
|
|
2024-07-15 23:22:04 +02:00
|
|
|
def node_to_texts_and_tags(node, substitutions)
|
2024-03-02 22:41:05 +01:00
|
|
|
case node
|
|
|
|
in type: 'paragraph', content:
|
2024-07-15 23:22:04 +02:00
|
|
|
children_texts_and_tags(content, substitutions)
|
|
|
|
in type: 'paragraph' # empty paragraph
|
|
|
|
''
|
|
|
|
in type: 'text', text:
|
2024-03-02 22:41:05 +01:00
|
|
|
text.strip
|
2024-07-15 23:22:04 +02:00
|
|
|
in type: 'mention', attrs: { id:, label: }
|
|
|
|
if substitutions.present?
|
|
|
|
substitutions.fetch(id) { "--#{id}--" }
|
|
|
|
else
|
|
|
|
"<span class='fr-tag fr-tag--sm'>#{label}</span>"
|
|
|
|
end
|
2024-03-02 22:41:05 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-18 12:38:46 +01:00
|
|
|
def children(content, substitutions, level)
|
|
|
|
content.map { node_to_html(_1, substitutions, level) }.join
|
2023-12-20 22:10:51 +01:00
|
|
|
end
|
|
|
|
|
2024-01-18 12:38:46 +01:00
|
|
|
def node_to_html(node, substitutions, level)
|
2024-02-02 13:49:22 +01:00
|
|
|
if level == 0 && !@body_started && node[:type].in?(['paragraph', 'heading']) && node.key?(:content)
|
2023-12-20 22:10:51 +01:00
|
|
|
@body_started = true
|
|
|
|
body_start_mark = " class=\"body-start\""
|
2023-10-20 22:50:39 +02:00
|
|
|
end
|
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
case node
|
|
|
|
in type: 'header', content:
|
2024-01-18 12:38:46 +01:00
|
|
|
"<header>#{children(content, substitutions, level + 1)}</header>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'footer', content:, **rest
|
2024-01-18 12:38:46 +01:00
|
|
|
"<footer#{text_align(rest[:attrs])}>#{children(content, substitutions, level + 1)}</footer>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'headerColumn', content:, **rest
|
2024-01-18 12:38:46 +01:00
|
|
|
"<div#{text_align(rest[:attrs])}>#{children(content, substitutions, level + 1)}</div>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'paragraph', content:, **rest
|
2024-01-18 12:38:46 +01:00
|
|
|
"<p#{body_start_mark}#{text_align(rest[:attrs])}>#{children(content, substitutions, level + 1)}</p>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'title', content:, **rest
|
2024-01-18 12:38:46 +01:00
|
|
|
"<h1#{text_align(rest[:attrs])}>#{children(content, substitutions, level + 1)}</h1>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'heading', attrs: { level: hlevel, **attrs }, content:
|
2024-02-02 13:49:22 +01:00
|
|
|
"<h#{hlevel}#{body_start_mark}#{text_align(attrs)}>#{children(content, substitutions, level + 1)}</h#{hlevel}>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'bulletList', content:
|
2024-01-18 12:38:46 +01:00
|
|
|
"<ul>#{children(content, substitutions, level + 1)}</ul>"
|
2024-07-25 11:42:50 +02:00
|
|
|
in type: 'orderedList', content:, **rest
|
|
|
|
"<ol#{class_list(rest[:attrs])}>#{children(content, substitutions, level + 1)}</ol>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'listItem', content:
|
2024-01-18 12:38:46 +01:00
|
|
|
"<li>#{children(content, substitutions, level + 1)}</li>"
|
2024-07-25 11:42:50 +02:00
|
|
|
in type: 'descriptionList', content:
|
|
|
|
"<dl>#{children(content, substitutions, level + 1)}</dl>"
|
2024-09-04 17:32:21 +02:00
|
|
|
in type: 'descriptionTerm', content:, **rest
|
|
|
|
"<dt#{class_list(rest[:attrs])}>#{children(content, substitutions, level + 1)}</dt>"
|
2024-07-25 11:42:50 +02:00
|
|
|
in type: 'descriptionDetails', content:
|
|
|
|
"<dd>#{children(content, substitutions, level + 1)}</dd>"
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'text', text:, **rest
|
|
|
|
if rest[:marks].present?
|
|
|
|
apply_marks(text, rest[:marks])
|
2023-10-20 22:50:39 +02:00
|
|
|
else
|
2023-12-20 22:10:51 +01:00
|
|
|
text
|
2023-10-20 22:50:39 +02:00
|
|
|
end
|
2023-12-20 22:10:51 +01:00
|
|
|
in type: 'mention', attrs: { id: }, **rest
|
2024-08-30 13:00:13 +02:00
|
|
|
text_or_presentation = substitutions.fetch(id) { "--#{id}--" }
|
|
|
|
text = if text_or_presentation.respond_to?(:to_tiptap_node)
|
|
|
|
handle_presentation_node(text_or_presentation, substitutions, level + 1)
|
2024-07-04 21:02:07 +02:00
|
|
|
else
|
2024-08-30 13:00:13 +02:00
|
|
|
text_or_presentation
|
2024-07-04 21:02:07 +02:00
|
|
|
end
|
2024-01-18 12:38:46 +01:00
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
if rest[:marks].present?
|
2024-01-18 12:38:46 +01:00
|
|
|
apply_marks(text, rest[:marks])
|
2023-12-20 22:10:51 +01:00
|
|
|
else
|
2024-01-18 12:38:46 +01:00
|
|
|
text
|
2023-12-20 22:10:51 +01:00
|
|
|
end
|
|
|
|
in { type: type } if ["paragraph", "title", "heading"].include?(type) && !node.key?(:content)
|
|
|
|
# noop
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-08-30 13:00:13 +02:00
|
|
|
def handle_presentation_node(presentation, substitutions, level)
|
|
|
|
node = presentation.to_tiptap_node
|
|
|
|
content = node_to_html(node, substitutions, level)
|
|
|
|
if presentation.block_level?
|
|
|
|
"</p>#{content}<p>"
|
|
|
|
else
|
|
|
|
content
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
def text_align(attrs)
|
|
|
|
if attrs.present? && attrs[:textAlign].present?
|
|
|
|
" style=\"text-align: #{attrs[:textAlign]}\""
|
|
|
|
else
|
|
|
|
""
|
2023-10-20 22:50:39 +02:00
|
|
|
end
|
2023-12-20 22:10:51 +01:00
|
|
|
end
|
2023-10-20 22:50:39 +02:00
|
|
|
|
2024-07-25 11:42:50 +02:00
|
|
|
def class_list(attrs)
|
|
|
|
if attrs.present? && attrs[:class].present?
|
|
|
|
" class=\"#{attrs[:class]}\""
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-12-20 22:10:51 +01:00
|
|
|
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>"
|
2023-10-20 22:50:39 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|