Current section

Files

Jump to
nldoc_conversion_reader_tiptap lib nldoc conversion reader tiptap.ex
Raw

lib/nldoc/conversion/reader/tiptap.ex

defmodule NLdoc.Conversion.Reader.Tiptap do
@moduledoc """
Reader for convert_elementing Tiptap elements to Spec Resources.
"""
alias NLdoc.Conversion.Reader.Tiptap.{Attribute, Mark}
alias NLdoc.Spec.{
BlockQuotation,
DefinitionDetails,
DefinitionList,
DefinitionTerm,
Document,
Heading,
Image,
Link,
ListItem,
OrderedList,
Paragraph,
Preformatted,
Table,
TableCell,
TableHeader,
TableRow,
Text,
UnorderedList
}
# TODO: Support descriptors
# This defines functions to be generated in the for-loops below.
#
# This is a tuple in format:
# 1. Element name in Tiptap.
# 2. Struct from Spec.
# 3. Attributes as keyword list in format {spec_attribute: tiptap_attribute}
# where spec_attribute is an atom and tiptap_attribute is a string.
@element_mappings [
{"doc", Document, [id: "id", assets: "assets"]},
# TODO: Support attribute caption
{"image", Image,
[id: "id", source: "assetId", alternative_text: "alt", decorative: "decorative"]},
# TODO: Support attributes cite, caption/footer
{"blockquote", BlockQuotation, [id: "id"]},
{"codeBlock", Preformatted, [id: "id"]},
{"heading", Heading, [id: "id", level: "level"]},
# TODO: Support attributes style type, reversed
{"orderedList", OrderedList, [id: "id", start: "start"]},
# TODO: Support attribute style type
{"bulletList", UnorderedList, [id: "id"]},
{"listItem", ListItem, [id: "id"]},
# TODO: Support attribute caption
{"table", Table, [id: "id"]},
{"tableRow", TableRow, [id: "id"]},
{"tableCell", TableCell, [id: "id", rowspan: "rowspan", colspan: "colspan"]},
# TODO: Support attributes abbreviation, scope
{"tableHeader", TableHeader, [id: "id", rowspan: "rowspan", colspan: "colspan"]},
{"definitionList", DefinitionList, [id: "id"]},
{"definitionTerm", DefinitionTerm, [id: "id"]},
{"definitionDetails", DefinitionDetails, [id: "id"]},
{"paragraph", Paragraph, [id: "id"]},
# Custom cases, will generate &convert_element/1 functions that won't be used.
# Used for setting the attributes.
{"_text", Text, [id: "id"]},
{"_link", Link, [id: "id", uri: "href", purpose: "purpose"]}
]
@type tiptap_attribute_value :: String.t() | number() | boolean()
@type tiptap_attribute :: {String.t(), tiptap_attribute_value()}
@doc """
Converts Tiptap document to a NLdoc document.
"""
@spec convert(map()) :: Document.t()
def convert(element = %{"type" => "doc"}),
do: element |> convert_element() |> hd()
@spec convert_element(map()) :: [NLdoc.Spec.object()]
defp convert_element(%{"type" => "text", "text" => text, "marks" => marks})
when is_binary(text) and text !== "" and is_list(marks),
do: %Text{text: text} |> Mark.apply(marks) |> post_process()
for {tiptap_type, struct_module, mappings} <- @element_mappings,
not String.starts_with?(tiptap_type, "_") do
if Map.has_key?(struct_module.__struct__(), :children) do
defp convert_element(element = %{"type" => unquote(tiptap_type)}) do
children =
element
|> Map.get("content", [])
|> Enum.flat_map(&convert_element/1)
%unquote(struct_module){}
|> Map.put(:children, children)
|> Attribute.apply(element |> Map.get("attrs", %{}), unquote(mappings))
|> post_process()
end
else
defp convert_element(element = %{"type" => unquote(tiptap_type)}),
do:
%unquote(struct_module){}
|> Attribute.apply(element |> Map.get("attrs", %{}), unquote(mappings))
|> post_process()
end
end
# Unsupported element
defp convert_element(_),
do: []
@spec post_process(t) :: [t] when t: term()
defp post_process(%Image{source: nil}),
do: []
defp post_process(element),
do: element |> List.wrap()
end