draftjs_html 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 570a70e02425c105494fa4abd36c9b51e1e667c4022446dbf83fa7c83b1e4d3d
4
- data.tar.gz: b6be5723042425ce04408ec2b996f94b30ab289354829b78eafd3fbde7d17ed1
3
+ metadata.gz: 4955cd2cbaade2ea9ae659cb65028e4097fb1d8d95d5058bc165e758f40e7794
4
+ data.tar.gz: 5bf0f0fbf4e6e73eb3da8b069fe536505a56db67cfed802c04baa2079a8641ff
5
5
  SHA512:
6
- metadata.gz: 3a1f74891f8942d709dda34cc9d3f2893cba092d09ca797a02c796a75aa314b7966856d5f80564304dd9d45b75e0a0ce649d792e82399af8d8429b6362f3e343
7
- data.tar.gz: 1ee7fe5a0df0073220a29acd055cf64ddfa988755c1ad53dd6f58822cad3ec7ffcff368fa8f89f3c972419b10a8b7a4e4a723466a9e5d37ca9bd7eecfe040e9e
6
+ metadata.gz: a36d06cdc9383a8c27d3d93c08fc59c4a022120aa9951cdf6e9774f0fc77f3e69439ea761a6765469b0f9a4c4879b76b216b465473894b35fbd7e57b5e5ff740
7
+ data.tar.gz: 16c84f495363d08c997b55de7f53b496d8f92760c936a755c1c1960d024bae7e9fb3235bcbcdb32d05649dc2668c16d4833577fce9b9861273a32f7cae33082a
@@ -2,12 +2,13 @@
2
2
 
3
3
  module DraftjsHtml
4
4
  module Draftjs
5
- Block = Struct.new(:key, :text, :type, :inline_style_ranges, :raw_entity_ranges, keyword_init: true) do
5
+ Block = Struct.new(:key, :text, :type, :depth, :inline_style_ranges, :raw_entity_ranges, keyword_init: true) do
6
6
  def self.parse(raw)
7
7
  new(
8
8
  key: raw['key'],
9
9
  text: raw['text'],
10
10
  type: raw['type'],
11
+ depth: raw['depth'],
11
12
  inline_style_ranges: Array(raw['inlineStyleRanges']),
12
13
  raw_entity_ranges: Array(raw['entityRanges']),
13
14
  )
@@ -0,0 +1,50 @@
1
+ module DraftjsHtml
2
+ module HtmlDefaults
3
+ BLOCK_TYPE_TO_HTML = {
4
+ 'unstyled' => 'p',
5
+ 'paragraph' => 'p',
6
+ 'header-one' => 'h1',
7
+ 'header-two' => 'h2',
8
+ 'header-three' => 'h3',
9
+ 'header-four' => 'h4',
10
+ 'header-five' => 'h5',
11
+ 'header-six' => 'h6',
12
+ 'blockquote' => 'blockquote',
13
+ 'code-block' => 'code',
14
+ 'ordered-list-item' => 'li',
15
+ 'unordered-list-item' => 'li',
16
+ 'atomic' => 'figure',
17
+ }.freeze
18
+
19
+ STYLE_MAP = {
20
+ 'BOLD' => 'b',
21
+ 'ITALIC' => 'i',
22
+ 'STRIKETHROUGH' => 'del',
23
+ 'UNDERLINE' => 'u',
24
+ }.freeze
25
+
26
+ ENTITY_ATTRIBUTE_NAME_MAP = {
27
+ 'className' => 'class',
28
+ 'url' => 'href',
29
+ }.freeze
30
+
31
+ DEFAULT_ENTITY_STYLE_FN = ->(_entity, chars, _doc) { chars }
32
+
33
+ ENTITY_CONVERSION_MAP = {
34
+ 'LINK' => ->(entity, content, *) {
35
+ attributes = entity.data.slice('url', 'rel', 'target', 'title', 'className').each_with_object({}) do |(attr, value), h|
36
+ h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
37
+ end
38
+
39
+ DraftjsHtml::Node.new('a', attributes, content)
40
+ },
41
+ 'IMAGE' => ->(entity, *) {
42
+ attributes = entity.data.slice('src', 'alt', 'className', 'width', 'height').each_with_object({}) do |(attr, value), h|
43
+ h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
44
+ end
45
+
46
+ DraftjsHtml::Node.new('img', attributes)
47
+ }
48
+ }.freeze
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ module DraftjsHtml
2
+ # This class manages the depth and nesting of the myriad HTML tags generated by DraftjsHtml::ToHtml.
3
+ # It is intended to be a private implementation detail.
4
+ class HtmlDepth # :nodoc:
5
+ BLOCK_TYPE_TO_HTML_WRAPPER = {
6
+ 'code-block' => 'pre',
7
+ 'ordered-list-item' => 'ol',
8
+ 'unordered-list-item' => 'ul',
9
+ }.freeze
10
+
11
+ attr_reader :body
12
+
13
+ def initialize(body)
14
+ @current_depth = 0
15
+ @body = body
16
+ @previous_parents = [body.parent]
17
+ end
18
+
19
+ def apply(block)
20
+ new_wrapper_tag = BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
21
+ if body.parent.name != new_wrapper_tag || block.depth != @current_depth
22
+ if @current_depth < block.depth
23
+ push_depth(body, new_wrapper_tag)
24
+ elsif @current_depth > block.depth
25
+ pop_depth(body, times: @current_depth - block.depth)
26
+ pop_nesting(body) unless new_wrapper_tag
27
+ elsif new_wrapper_tag
28
+ push_nesting(body, new_wrapper_tag)
29
+ elsif @previous_parents.size > 1
30
+ pop_nesting(body)
31
+ end
32
+ @current_depth = block.depth
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def push_depth(builder, tagname)
39
+ @previous_parents << builder.parent
40
+ builder.parent = builder.parent.last_element_child
41
+ push_nesting(builder, tagname)
42
+ end
43
+
44
+ def push_nesting(builder, tagname)
45
+ node = create_child(builder, tagname)
46
+ @previous_parents << builder.parent
47
+ builder.parent = node
48
+ end
49
+
50
+ def pop_depth(builder, times:)
51
+ times.times do
52
+ pop_nesting(builder)
53
+ pop_nesting(builder)
54
+ end
55
+ end
56
+
57
+ def pop_nesting(builder)
58
+ builder.parent = @previous_parents.pop
59
+ end
60
+
61
+ def create_child(builder, tagname)
62
+ builder.parent.add_child(builder.doc.create_element(tagname))
63
+ end
64
+ end
65
+ end
@@ -18,7 +18,7 @@ module DraftjsHtml
18
18
  lines = raw.lines
19
19
  text_nodes = lines.flat_map.with_index do |text, i|
20
20
  nodes = [Nokogiri::XML::Text.new(text.chomp, document)]
21
- nodes << Nokogiri::XML::Node.new('br', document) if i < lines.size - 1
21
+ nodes << Nokogiri::XML::Node.new('br', document) if text.end_with?("\n")
22
22
  nodes
23
23
  end
24
24
 
@@ -0,0 +1,25 @@
1
+ module DraftjsHtml
2
+ class OverrideableMap
3
+ def initialize(defaults = {})
4
+ @map = defaults.dup.transform_keys(&:to_s)
5
+ end
6
+
7
+ def with_overrides(overrides)
8
+ @map.merge!((overrides || {}).transform_keys(&:to_s))
9
+ self
10
+ end
11
+
12
+ def with_default(default)
13
+ @default = default
14
+ self
15
+ end
16
+
17
+ def value_of!(key)
18
+ @map.fetch(key.to_s)
19
+ end
20
+
21
+ def value_of(key)
22
+ @map.fetch(key.to_s, @default)
23
+ end
24
+ end
25
+ end
@@ -1,59 +1,14 @@
1
1
  require_relative 'node'
2
+ require_relative 'html_depth'
3
+ require_relative 'html_defaults'
4
+ require_relative 'overrideable_map'
2
5
 
3
6
  module DraftjsHtml
4
7
  class ToHtml
5
- BLOCK_TYPE_TO_HTML = {
6
- 'unstyled' => 'p',
7
- 'paragraph' => 'p',
8
- 'header-one' => 'h1',
9
- 'header-two' => 'h2',
10
- 'header-three' => 'h3',
11
- 'header-four' => 'h4',
12
- 'header-five' => 'h5',
13
- 'header-six' => 'h6',
14
- 'blockquote' => 'blockquote',
15
- 'code-block' => 'code',
16
- 'ordered-list-item' => 'li',
17
- 'unordered-list-item' => 'li',
18
- 'atomic' => 'figure',
19
- }.freeze
20
- BLOCK_TYPE_TO_HTML_WRAPPER = {
21
- 'code-block' => 'pre',
22
- 'ordered-list-item' => 'ol',
23
- 'unordered-list-item' => 'ul',
24
- }.freeze
25
- STYLE_MAP = {
26
- 'BOLD' => 'b',
27
- 'ITALIC' => 'i',
28
- 'STRIKETHROUGH' => 'del',
29
- 'UNDERLINE' => 'u',
30
- }.freeze
31
-
32
- DEFAULT_ENTITY_STYLE_FN = ->(_entity, chars, _doc) { chars }
33
- ENTITY_ATTRIBUTE_NAME_MAP = {
34
- 'className' => 'class',
35
- 'url' => 'href',
36
- }.freeze
37
- ENTITY_CONVERSION_MAP = {
38
- 'LINK' => ->(entity, content, *) {
39
- attributes = entity.data.slice('url', 'rel', 'target', 'title', 'className').each_with_object({}) do |(attr, value), h|
40
- h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
41
- end
42
-
43
- DraftjsHtml::Node.new('a', attributes, content)
44
- },
45
- 'IMAGE' => ->(entity, *) {
46
- attributes = entity.data.slice('src', 'alt', 'className', 'width', 'height').each_with_object({}) do |(attr, value), h|
47
- h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
48
- end
49
-
50
- DraftjsHtml::Node.new('img', attributes)
51
- }
52
- }.freeze
53
-
54
8
  def initialize(options)
55
9
  @options = ensure_options!(options)
56
10
  @document = Nokogiri::HTML::Builder.new(encoding: @options.fetch(:encoding, 'UTF-8'))
11
+ @html_depth = HtmlDepth.new(@document)
57
12
  end
58
13
 
59
14
  def convert(raw_draftjs)
@@ -61,10 +16,10 @@ module DraftjsHtml
61
16
 
62
17
  @document.html do |html|
63
18
  html.body do |body|
64
- @previous_parent = body.parent
19
+ @previous_parents = [body.parent]
65
20
 
66
21
  draftjs.blocks.each do |block|
67
- ensure_nesting_depth(block, body)
22
+ @html_depth.apply(block)
68
23
 
69
24
  body.public_send(block_element_for(block)) do |block_body|
70
25
  block.each_range do |char_range|
@@ -84,18 +39,7 @@ module DraftjsHtml
84
39
  private
85
40
 
86
41
  def squeeze_newlines(char_range)
87
- char_range.text = @options[:newline_squeezer].call(char_range.text)
88
- end
89
-
90
- def ensure_nesting_depth(block, body)
91
- new_wrapper_tag = BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
92
- if body.parent.name != new_wrapper_tag
93
- if new_wrapper_tag
94
- push_nesting(body, new_wrapper_tag)
95
- else
96
- pop_nesting(body)
97
- end
98
- end
42
+ char_range.text = @options[:newline_squeezer].call(char_range.text.chomp)
99
43
  end
100
44
 
101
45
  def apply_styles_to(html, style_names, child)
@@ -117,43 +61,33 @@ module DraftjsHtml
117
61
  def block_element_for(block)
118
62
  return 'br' if block.blank?
119
63
 
120
- @options[:block_type_mapping].fetch(block.type)
64
+ @options[:block_type_mapping].value_of!(block.type)
121
65
  end
122
66
 
123
67
  def style_element_for(style)
124
- @options[:inline_style_mapping][style]
68
+ @options[:inline_style_mapping].value_of!(style)
125
69
  end
126
70
 
127
71
  def try_apply_entity_to(draftjs, char_range)
128
72
  entity = draftjs.find_entity(char_range.entity_key)
129
73
  content = char_range.text
130
74
  if entity
131
- style_fn = (@options[:entity_style_mappings][entity.type] || DEFAULT_ENTITY_STYLE_FN)
75
+ style_fn = @options[:entity_style_mappings].value_of(entity.type)
132
76
  content = style_fn.call(entity, Node.of(content), @document.parent)
133
77
  end
134
78
 
135
79
  content
136
80
  end
137
81
 
138
- def push_nesting(builder, tagname)
139
- node = create_child(builder, tagname)
140
- @previous_parent = builder.parent
141
- builder.parent = node
142
- end
143
-
144
- def pop_nesting(builder)
145
- builder.parent = @previous_parent
146
- end
147
-
148
- def create_child(builder, tagname)
149
- builder.parent.add_child(builder.doc.create_element(tagname))
150
- end
151
-
152
82
  def ensure_options!(opts)
153
- opts[:entity_style_mappings] = ENTITY_CONVERSION_MAP.merge(opts[:entity_style_mappings] || {}).transform_keys(&:to_s)
154
- opts[:block_type_mapping] = BLOCK_TYPE_TO_HTML.merge(opts[:block_type_mapping] || {})
83
+ opts[:entity_style_mappings] = OverrideableMap.new(HtmlDefaults::ENTITY_CONVERSION_MAP)
84
+ .with_overrides(opts[:entity_style_mappings])
85
+ .with_default(HtmlDefaults::DEFAULT_ENTITY_STYLE_FN)
86
+ opts[:block_type_mapping] = OverrideableMap.new(HtmlDefaults::BLOCK_TYPE_TO_HTML)
87
+ .with_overrides(opts[:block_type_mapping])
155
88
  opts[:newline_squeezer] = opts[:squeeze_newlines] ? ->(text) { text.gsub(/(\n|\r\n)+/, "\n") } : ->(text) { text }
156
- opts[:inline_style_mapping] = STYLE_MAP.merge(opts[:inline_style_mapping] || {}).transform_keys(&:to_s)
89
+ opts[:inline_style_mapping] = OverrideableMap.new(HtmlDefaults::STYLE_MAP)
90
+ .with_overrides(opts[:inline_style_mapping])
157
91
  opts[:inline_style_renderer] ||= ->(*) { nil }
158
92
  opts
159
93
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DraftjsHtml
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draftjs_html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TJ Taylor
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-07 00:00:00.000000000 Z
11
+ date: 2022-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -65,7 +65,10 @@ files:
65
65
  - lib/draftjs_html/draftjs/entity.rb
66
66
  - lib/draftjs_html/draftjs/entity_map.rb
67
67
  - lib/draftjs_html/draftjs/to_raw.rb
68
+ - lib/draftjs_html/html_defaults.rb
69
+ - lib/draftjs_html/html_depth.rb
68
70
  - lib/draftjs_html/node.rb
71
+ - lib/draftjs_html/overrideable_map.rb
69
72
  - lib/draftjs_html/to_html.rb
70
73
  - lib/draftjs_html/version.rb
71
74
  homepage: https://github.com/dugancathal/draftjs_html
@@ -75,7 +78,7 @@ metadata:
75
78
  homepage_uri: https://github.com/dugancathal/draftjs_html
76
79
  source_code_uri: https://github.com/dugancathal/draftjs_html
77
80
  changelog_uri: https://github.com/dugancathal/draftjs_html/tree/main/CHANGELOG.md
78
- post_install_message:
81
+ post_install_message:
79
82
  rdoc_options: []
80
83
  require_paths:
81
84
  - lib
@@ -90,8 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
93
  - !ruby/object:Gem::Version
91
94
  version: '0'
92
95
  requirements: []
93
- rubygems_version: 3.1.6
94
- signing_key:
96
+ rubygems_version: 3.1.2
97
+ signing_key:
95
98
  specification_version: 4
96
99
  summary: A tool for converting DraftJS JSON to HTML (and back again)
97
100
  test_files: []