draftjs_html 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: []