draftjs_html 0.7.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 +4 -4
- data/README.md +14 -4
- data/lib/draftjs_html/draftjs/block.rb +2 -1
- data/lib/draftjs_html/html_defaults.rb +50 -0
- data/lib/draftjs_html/html_depth.rb +65 -0
- data/lib/draftjs_html/node.rb +1 -1
- data/lib/draftjs_html/overrideable_map.rb +25 -0
- data/lib/draftjs_html/to_html.rb +20 -80
- data/lib/draftjs_html/version.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4955cd2cbaade2ea9ae659cb65028e4097fb1d8d95d5058bc165e758f40e7794
|
4
|
+
data.tar.gz: 5bf0f0fbf4e6e73eb3da8b069fe536505a56db67cfed802c04baa2079a8641ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a36d06cdc9383a8c27d3d93c08fc59c4a022120aa9951cdf6e9774f0fc77f3e69439ea761a6765469b0f9a4c4879b76b216b465473894b35fbd7e57b5e5ff740
|
7
|
+
data.tar.gz: 16c84f495363d08c997b55de7f53b496d8f92760c936a755c1c1960d024bae7e9fb3235bcbcdb32d05649dc2668c16d4833577fce9b9861273a32f7cae33082a
|
data/README.md
CHANGED
@@ -77,6 +77,17 @@ HTML, it's also possible to return `Nokogiri::Node` objects or String objects.
|
|
77
77
|
Specify the HTML generation encoding.
|
78
78
|
Defaults to `UTF-8`.
|
79
79
|
|
80
|
+
#### `squeeze_newlines`
|
81
|
+
|
82
|
+
Often times, we'll get text in our blocks that will generate unexpected HTML.
|
83
|
+
Most of this is caused by whitespace.
|
84
|
+
You can use the `squeeze_newlines` option to collapse consecutive newline/CRLF characters to one, resulting in a single `<br>` tag.
|
85
|
+
Defaults to `false`.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
|
89
|
+
```
|
90
|
+
|
80
91
|
#### `:entity_style_mappings`
|
81
92
|
|
82
93
|
Allows the author to specify special mapping functions for entities.
|
@@ -93,12 +104,11 @@ These may be overridden and appended to, like so:
|
|
93
104
|
|
94
105
|
```ruby
|
95
106
|
DraftjsHtml.to_html(raw_draftjs, options: {
|
96
|
-
|
97
|
-
'unstyled' => 'span',
|
98
|
-
},
|
107
|
+
squeeze_newlines: true,
|
99
108
|
})
|
100
109
|
|
101
|
-
#
|
110
|
+
# Given a DraftJS block like: `{ text: 'Hi!\n\n\nWelcome to Westeros!\n\n\n'}`
|
111
|
+
# This would generate `<p>Hi!<br>Welcome to Westeros!<br></p>`
|
102
112
|
```
|
103
113
|
|
104
114
|
#### `:inline_style_mapping`
|
@@ -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
|
data/lib/draftjs_html/node.rb
CHANGED
@@ -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
|
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
|
data/lib/draftjs_html/to_html.rb
CHANGED
@@ -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,13 +16,14 @@ module DraftjsHtml
|
|
61
16
|
|
62
17
|
@document.html do |html|
|
63
18
|
html.body do |body|
|
64
|
-
@
|
19
|
+
@previous_parents = [body.parent]
|
65
20
|
|
66
21
|
draftjs.blocks.each do |block|
|
67
|
-
|
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|
|
26
|
+
squeeze_newlines(char_range)
|
71
27
|
content = try_apply_entity_to(draftjs, char_range)
|
72
28
|
|
73
29
|
apply_styles_to(block_body, char_range.style_names, Node.of(content))
|
@@ -82,15 +38,8 @@ module DraftjsHtml
|
|
82
38
|
|
83
39
|
private
|
84
40
|
|
85
|
-
def
|
86
|
-
|
87
|
-
if body.parent.name != new_wrapper_tag
|
88
|
-
if new_wrapper_tag
|
89
|
-
push_nesting(body, new_wrapper_tag)
|
90
|
-
else
|
91
|
-
pop_nesting(body)
|
92
|
-
end
|
93
|
-
end
|
41
|
+
def squeeze_newlines(char_range)
|
42
|
+
char_range.text = @options[:newline_squeezer].call(char_range.text.chomp)
|
94
43
|
end
|
95
44
|
|
96
45
|
def apply_styles_to(html, style_names, child)
|
@@ -112,42 +61,33 @@ module DraftjsHtml
|
|
112
61
|
def block_element_for(block)
|
113
62
|
return 'br' if block.blank?
|
114
63
|
|
115
|
-
@options[:block_type_mapping].
|
64
|
+
@options[:block_type_mapping].value_of!(block.type)
|
116
65
|
end
|
117
66
|
|
118
67
|
def style_element_for(style)
|
119
|
-
@options[:inline_style_mapping]
|
68
|
+
@options[:inline_style_mapping].value_of!(style)
|
120
69
|
end
|
121
70
|
|
122
71
|
def try_apply_entity_to(draftjs, char_range)
|
123
72
|
entity = draftjs.find_entity(char_range.entity_key)
|
124
73
|
content = char_range.text
|
125
74
|
if entity
|
126
|
-
style_fn =
|
75
|
+
style_fn = @options[:entity_style_mappings].value_of(entity.type)
|
127
76
|
content = style_fn.call(entity, Node.of(content), @document.parent)
|
128
77
|
end
|
129
78
|
|
130
79
|
content
|
131
80
|
end
|
132
81
|
|
133
|
-
def push_nesting(builder, tagname)
|
134
|
-
node = create_child(builder, tagname)
|
135
|
-
@previous_parent = builder.parent
|
136
|
-
builder.parent = node
|
137
|
-
end
|
138
|
-
|
139
|
-
def pop_nesting(builder)
|
140
|
-
builder.parent = @previous_parent
|
141
|
-
end
|
142
|
-
|
143
|
-
def create_child(builder, tagname)
|
144
|
-
builder.parent.add_child(builder.doc.create_element(tagname))
|
145
|
-
end
|
146
|
-
|
147
82
|
def ensure_options!(opts)
|
148
|
-
opts[:entity_style_mappings] =
|
149
|
-
|
150
|
-
|
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])
|
88
|
+
opts[:newline_squeezer] = opts[:squeeze_newlines] ? ->(text) { text.gsub(/(\n|\r\n)+/, "\n") } : ->(text) { text }
|
89
|
+
opts[:inline_style_mapping] = OverrideableMap.new(HtmlDefaults::STYLE_MAP)
|
90
|
+
.with_overrides(opts[:inline_style_mapping])
|
151
91
|
opts[:inline_style_renderer] ||= ->(*) { nil }
|
152
92
|
opts
|
153
93
|
end
|
data/lib/draftjs_html/version.rb
CHANGED
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.
|
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-
|
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.
|
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: []
|