Class: Y::Lexxy

Inherits:
Lexical show all
Defined in:
lib/y/lexxy.rb

Overview

The Lexxy renderer: Y::Lexical (core Lexical) plus the Lexxy-specific schema, applied beneath the app's rules — an app rule for one of these types simply replaces it. This is the byte-parity class: the fixture tests hold Y::Lexxy.new(doc).to_html identical to a live editor's own serialized value.

The schema doubles as the reference for augmenting a renderer: simple nodes are declarative hashes, nodes with logic are plain methods mapped in NODES.

Constant Summary collapse

NODES =
{
  # Lexxy's replacement for Lexical's CodeNode.
  "early_escape_code" => { tag: "pre", attrs: { "data-language" => :language } },
  "horizontal_divider" => { tag: "hr", void: true },
  "provisonal_paragraph" => method(:provisional_paragraph), # (sic: Lexxy's spelling)
  "image_gallery" => method(:gallery),
  "table" => { contains: :blocks, render: method(:table) },
  "wrapped_table_node" => { contains: :blocks, render: method(:table) },
  "tablecell" => { contains: :blocks, render: method(:table_cell) },
  "listitem" => { contains: :blocks, render: method(:list_item) },
  "action_text_attachment" => method(:upload),
  "custom_action_text_attachment" => method(:mention)
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Lexical

#node_types, #to_html

Constructor Details

#initialize(doc, nodes: {}) ⇒ Lexxy

Returns a new instance of Lexxy.



96
97
98
# File 'lib/y/lexxy.rb', line 96

def initialize(doc, nodes: {}, &)
  super(doc, nodes: NODES.merge(nodes.transform_keys(&:to_s)), &)
end

Class Method Details

.attachment_attr(node, html_name, stored) ⇒ Object

A stored nil (unset) is skipped; a stored empty string still emits.



76
77
78
79
80
# File 'lib/y/lexxy.rb', line 76

def self.attachment_attr(node, html_name, stored)
  return "" if node.attrs[stored].nil?

  %( #{html_name}="#{RenderRules.escape_attr(node.attrs[stored])}")
end

Adjacent previewable images; the class carries the image count (ActionText's convention).



21
22
23
# File 'lib/y/lexxy.rb', line 21

def self.gallery(node)
  %(<div class="attachment-gallery attachment-gallery--#{node.child_types.length}">#{node.content}</div>)
end

.list_item(node) ⇒ Object

Attribute order follows Lexxy's export — checked items put aria-checked before value; items holding a nested list append the lexxy-nested-listitem class after value.



42
43
44
45
46
47
48
49
# File 'lib/y/lexxy.rb', line 42

def self.list_item(node)
  out = +"<li"
  checked = node.attrs["__checked"]
  out << %( aria-checked="#{checked}") unless checked.nil?
  out << %( value="#{node.attrs["__value"] || 1}")
  out << %( class="lexxy-nested-listitem") if node.child_types.include?("list")
  "#{out}>#{node.content}</li>"
end

.mention(node) ⇒ Object

A content attachment (mention, embed): content carries the escaped inner HTML; plainText is not exported.



67
68
69
70
71
72
73
# File 'lib/y/lexxy.rb', line 67

def self.mention(node)
  out = +"<action-text-attachment"
  out << attachment_attr(node, "sgid", "sgid")
  out << attachment_attr(node, "content", "innerHtml")
  out << attachment_attr(node, "content-type", "contentType")
  "#{out}></action-text-attachment>"
end

.provisional_paragraph(node) ⇒ Object

A cursor-placement placeholder; empty ones export to nothing.



15
16
17
# File 'lib/y/lexxy.rb', line 15

def self.provisional_paragraph(node)
  node.content.empty? ? "" : "<p>#{node.content}</p>"
end

.table(node) ⇒ Object

Lexxy wraps tables in a styled figure.



26
27
28
# File 'lib/y/lexxy.rb', line 26

def self.table(node)
  %(<figure class="lexxy-content__table-wrapper"><table><tbody>#{node.content}</tbody></table></figure>)
end

.table_cell(node) ⇒ Object

Class + background match Lexxy's own header-cell export.



31
32
33
34
35
36
37
# File 'lib/y/lexxy.rb', line 31

def self.table_cell(node)
  header = node.attrs["__headerState"].is_a?(Numeric) && node.attrs["__headerState"].positive?
  return "<td>#{node.content}</td>" unless header

  style = %(style="background-color: rgb(242, 243, 245);")
  %(<th class="lexxy-content__table-cell--header" #{style}>#{node.content}</th>)
end

.upload(node) ⇒ Object

An upload, in the exact shape ActionText round-trips: attribute order and presence mirror Lexxy's exportDOM (nulls omitted, previewable only when true, presentation="gallery" always).



54
55
56
57
58
59
60
61
62
63
# File 'lib/y/lexxy.rb', line 54

def self.upload(node)
  out = +"<action-text-attachment"
  out << attachment_attr(node, "sgid", "sgid")
  out << %( previewable="true") if node.attrs["previewable"] == true
  [%w[url src], %w[alt altText], %w[caption caption],
   %w[content-type contentType], %w[filename fileName],
   %w[filesize fileSize], %w[width width], %w[height height]]
    .each { |html_name, stored| out << attachment_attr(node, html_name, stored) }
  %(#{out} presentation="gallery"></action-text-attachment>)
end