draftjs_html 0.20.0 → 0.22.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: 4b5496551a743763154bdec10a56591d3369eb6361c863ebc0e2defd95dd539e
4
- data.tar.gz: 714eb86046e0008bc944a18edcb4137ec5034401c9a5df7e02140a7b634289c7
3
+ metadata.gz: 84ac3de7f21860685dbf39a24fb921227a1ccce6d97940acd279976daab15970
4
+ data.tar.gz: 2d3511eef3134a6cc01cd3da0147b3c347209c6c90ef4f5aaea90c681af979b1
5
5
  SHA512:
6
- metadata.gz: a90d95f982a2bc56c1ab87326aa9226229ed8676920cd26c01f6cf3711910f85405c802fb8c707bec1ecae16b6d86dcd750dd92c9bab2fb7dafd158ddeaa7ecb
7
- data.tar.gz: 7232b75cb4d677c5cc9ea3e38e5d605a4d3d7bde1ec7842405b025e21fa2d5145a2317eebb1a72f88a0555c63123bb660c695750b2111ac60ec9fb5b27875a2d
6
+ metadata.gz: 2775eac524063149973d4908f9f30f5e9a4d6b734f35e44b459454a95cc6a415a25a1b36daf07af3b34b1bf08b2b96c2980e6be12a9b21ed23c162100688ad03
7
+ data.tar.gz: dd0c42f5271934a1a17452159e7dbad8b51bd4ad0637a9b87cf4c77b43c6ea690bdda838830d92b1fcd929e985d88958fa5bd5326af89dc3e9db76e7e84a5fe2
data/README.md CHANGED
@@ -127,6 +127,20 @@ DraftjsHtml.to_html(raw_draftjs, options: {
127
127
  # This would generate <strong> tags instead of <b> tags around ranges of `BOLD` inline styles.
128
128
  ```
129
129
 
130
+ You may also add attributes to tags created by `inline_style_mapping`s by using a two element array.
131
+ The first element should be the tagname and the second argument a hash of attributes to values, like this:
132
+
133
+ ```ruby
134
+
135
+ DraftjsHtml.to_html(raw_draftjs, options: {
136
+ inline_style_mapping: {
137
+ 'BOLD' => ['strong', style: 'font-weight: 900'],
138
+ },
139
+ })
140
+ ```
141
+
142
+ # This would generate <strong> tags instead of <b> tags around ranges of `BOLD` inline styles.
143
+
130
144
  #### `:inline_style_renderer`
131
145
 
132
146
  If the direct mapping from `:inline_style_mapping` isn't enough, you can supply a custom function for rendering a style range.
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+
4
+ module DraftjsHtml
5
+ class FromHtml < Nokogiri::XML::SAX::Document
6
+ class CharList
7
+ module FinishableRange
8
+ def range
9
+ start..finish
10
+ end
11
+
12
+ def try_finish(index)
13
+ self[:finish] ||= index
14
+ end
15
+ end
16
+
17
+ Char = Struct.new(:it, :styles, :entity, :atomic, keyword_init: true) do
18
+ def atomic?
19
+ self.atomic
20
+ end
21
+
22
+ def styles
23
+ self[:styles] ||= Set.new
24
+ end
25
+ end
26
+
27
+ EntityRange = Struct.new(:entity, :start, :finish, keyword_init: true) do
28
+ include FinishableRange
29
+ end
30
+
31
+ StyleRange = Struct.new(:style, :start, :finish, keyword_init: true) do
32
+ include FinishableRange
33
+ end
34
+
35
+ attr_reader :chars
36
+
37
+ def initialize(initial = [])
38
+ @chars = initial.dup
39
+ end
40
+
41
+ def append(str, styles: Set.new, entity: nil)
42
+ @chars << Char.new(it: "\n") if @chars.last&.atomic?
43
+
44
+ @chars += str.chars.map { Char.new(it: _1, styles: Set.new(styles), entity: entity) }
45
+ end
46
+
47
+ def append_char(char)
48
+ @chars << char
49
+ end
50
+
51
+ def text
52
+ @chars.map(&:it).join
53
+ end
54
+
55
+ def size
56
+ @chars.size
57
+ end
58
+
59
+ def any?
60
+ size > 0
61
+ end
62
+
63
+ def atomic?
64
+ @chars.any? && @chars.all?(&:atomic?)
65
+ end
66
+
67
+ def each_line
68
+ return to_enum(:each_line) unless block_given?
69
+
70
+ line = self.class.new
71
+ chars.each do |c|
72
+ if c.it == "\n"
73
+ yield line
74
+ line = self.class.new
75
+ next
76
+ end
77
+
78
+ line.append_char(c)
79
+ end
80
+
81
+ yield line if line.any?
82
+ end
83
+
84
+ def apply_entity(range, entity)
85
+ @chars[range].each { _1.entity = entity }
86
+ end
87
+
88
+ def append_atomic_entity(entity)
89
+ append("\n") if @chars.any?
90
+ append_char(Char.new(it: ' ', atomic: true, entity: entity))
91
+ end
92
+
93
+ def append_styles(range, styles)
94
+ @chars[range].each { _1.styles += Array(styles) }
95
+ end
96
+
97
+ def +(other)
98
+ self.class.new(@chars + other.chars)
99
+ end
100
+
101
+ def entity_ranges
102
+ current_entity = nil
103
+
104
+ entity_ranges = @chars.each_with_object(Array.new).with_index do |(char, ranges), i|
105
+ next if char.entity == current_entity
106
+
107
+ current_entity = char.entity
108
+ ranges.last&.try_finish(i - 1)
109
+ ranges << EntityRange.new(entity: char.entity, start: i) if char.entity
110
+ end
111
+
112
+ entity_ranges.last&.try_finish(@chars.size - 1)
113
+ entity_ranges
114
+ end
115
+
116
+ def style_ranges
117
+ style_ranges = @chars.each_with_object({}).with_index do |(char, ranges_by_style), i|
118
+ char.styles.each do |style|
119
+ ranges_by_style[style] ||= []
120
+ if i > 0 && @chars[i-1].styles.include?(style)
121
+ ranges_by_style[style].last << i
122
+ else
123
+ ranges_by_style[style] << [i]
124
+ end
125
+ end
126
+ end
127
+
128
+ style_ranges.flat_map do |style, ranges|
129
+ ranges.map do |range|
130
+ StyleRange.new(style: style, start: range.first, finish: range.last)
131
+ end
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def find_overlapping_styles(descriptors)
138
+ descriptors.select do |candidate_a|
139
+ candidate_range = candidate_a[:start]..candidate_a[:finish]
140
+ (descriptors - [candidate_a]).any? do |other|
141
+ other_range = other[:start]..other[:finish]
142
+ range_overlaps?(candidate_range, other_range)
143
+ end
144
+ end
145
+ end
146
+
147
+ def range_overlaps?(candidate_range, other_range)
148
+ other_range.begin == candidate_range.begin || candidate_range.cover?(other_range.begin) || other_range.cover?(candidate_range.begin)
149
+ end
150
+ end
151
+ end
152
+ end
@@ -5,7 +5,7 @@ module DraftjsHtml
5
5
  @stack = []
6
6
  @nodes = []
7
7
  @list_depth = -1
8
- @style_stack = StyleStack.new
8
+ @active_styles = []
9
9
  end
10
10
 
11
11
  def push(tagname, attrs)
@@ -26,8 +26,7 @@ module DraftjsHtml
26
26
  @nodes.pop
27
27
  end
28
28
  blocks.reverse_each do |pending_block|
29
- pending_block.flush_to(draftjs, @style_stack)
30
- pending_block.apply_entities_to(draftjs)
29
+ pending_block.flush_to(draftjs)
31
30
  end
32
31
  @list_depth -= 1
33
32
  end
@@ -59,34 +58,35 @@ module DraftjsHtml
59
58
  next unless user_created_entity
60
59
 
61
60
  if content == '' && !user_created_entity[:atomic]
62
- current.text_buffer << ' '
63
- range = range.begin..(range.end+1)
61
+ current.text_buffer.append(' ', entity: user_created_entity)
62
+ elsif content == '' && user_created_entity[:atomic]
63
+ current.text_buffer.append_atomic_entity(user_created_entity)
64
+ else
65
+ current.text_buffer.apply_entity(range, user_created_entity)
64
66
  end
65
- current.entities << user_created_entity.merge(start: range.begin, finish: range.end)
66
67
  end
67
68
  end
68
69
 
69
70
  def style_start(tagname)
70
- @style_stack.track_start(tagname, current_character_offset + 1)
71
+ @active_styles += [DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]]
71
72
  end
72
73
 
73
74
  def style_end(tagname)
74
- @style_stack.track_end(tagname, current_character_offset)
75
+ @active_styles.delete_at(@active_styles.index(DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]))
75
76
  end
76
77
 
77
78
  def flush_to(draftjs)
78
- current.flush_to(draftjs, @style_stack)
79
- current.apply_entities_to(draftjs)
79
+ current.flush_to(draftjs)
80
80
  end
81
81
 
82
82
  def append_text(chars)
83
- current.text_buffer << chars unless chars.empty?
83
+ current.text_buffer.append(chars, styles: @active_styles) unless chars.empty?
84
84
  end
85
85
 
86
86
  private
87
87
 
88
88
  def current_text_buffer
89
- current.text_buffer.join
89
+ current.text_buffer.text
90
90
  end
91
91
 
92
92
  def current_character_offset
@@ -6,7 +6,7 @@ module DraftjsHtml
6
6
  tagname: name,
7
7
  attrs: attrs,
8
8
  entities: [],
9
- chars: [],
9
+ chars: CharList.new,
10
10
  pending_entities: [],
11
11
  depth: depth,
12
12
  parent_tagnames: parent_tagnames,
@@ -17,12 +17,8 @@ module DraftjsHtml
17
17
  self[:chars]
18
18
  end
19
19
 
20
- def clear_text_buffer
21
- self[:chars] = []
22
- end
23
-
24
20
  def character_offset
25
- text_buffer.join.length - 1
21
+ text_buffer.size - 1
26
22
  end
27
23
 
28
24
  def flushable?
@@ -36,31 +32,21 @@ module DraftjsHtml
36
32
  self.entities += other_pending_block.entities
37
33
  end
38
34
 
39
- def flush_to(draftjs, styles)
35
+ def flush_to(draftjs)
40
36
  if text_buffer.any?
41
- chars.join.lines.each do |line|
42
- draftjs.typed_block(block_name, line.chomp, depth: [depth, 0].max)
43
- end
44
-
45
- styles.each do |descriptor|
46
- finish = descriptor[:finish] || character_offset
47
- draftjs.inline_style(descriptor[:style], descriptor[:start]..finish)
48
- end
49
- end
37
+ text_buffer.each_line do |line|
38
+ block_type = line.atomic? ? 'atomic' : block_name
39
+ draftjs.typed_block(block_type, line.text, depth: [depth, 0].max)
50
40
 
51
- clear_text_buffer
52
- styles.clear_finished
53
- end
41
+ line.entity_ranges.each do |entity_range|
42
+ entity = entity_range.entity
43
+ draftjs.apply_entity entity[:type], entity_range.range, data: entity[:data], mutability: entity.fetch(:mutability, 'IMMUTABLE')
44
+ end
54
45
 
55
- def apply_entities_to(draftjs)
56
- Array(entities).each do |entity|
57
- range = entity[:start]..entity[:finish]
58
- if entity[:atomic]
59
- draftjs.typed_block('atomic', ' ', depth: [depth, 0].max)
60
- range = 0..0
46
+ line.style_ranges.each do |style_range|
47
+ draftjs.inline_style(style_range.style, style_range.range)
48
+ end
61
49
  end
62
-
63
- draftjs.apply_entity entity[:type], range, data: entity[:data], mutability: entity.fetch(:mutability, 'IMMUTABLE')
64
50
  end
65
51
  end
66
52
 
@@ -1,9 +1,9 @@
1
1
  require 'stringio'
2
2
  require_relative 'html_defaults'
3
3
  require_relative 'from_html/elements'
4
- require_relative 'from_html/style_stack'
5
4
  require_relative 'from_html/pending_block'
6
5
  require_relative 'from_html/depth_stack'
6
+ require_relative 'from_html/char_list'
7
7
 
8
8
  module DraftjsHtml
9
9
  class FromHtml < Nokogiri::XML::SAX::Document
@@ -29,7 +29,10 @@ module DraftjsHtml
29
29
  'code' => 'CODE',
30
30
  }.freeze
31
31
 
32
- STYLE_MAP = HTML_STYLE_TAGS_TO_STYLE.invert.freeze
32
+ STYLE_MAP = HTML_STYLE_TAGS_TO_STYLE.invert.merge(
33
+ 'HIGHLIGHT' => 'em',
34
+ 'RTL' => ['div', dir: 'rtl']
35
+ ).freeze
33
36
 
34
37
  ENTITY_ATTRIBUTE_NAME_MAP = {
35
38
  'className' => 'class',
@@ -49,7 +49,8 @@ module DraftjsHtml
49
49
  return append_child(html, custom_render_content) if custom_render_content
50
50
 
51
51
  style, *rest = style_names
52
- html.public_send(style_element_for(style)) do |builder|
52
+
53
+ html.public_send(*style_element_for(style)) do |builder|
53
54
  apply_styles_to(builder, rest, child)
54
55
  end
55
56
  end
@@ -78,7 +79,7 @@ module DraftjsHtml
78
79
  end
79
80
 
80
81
  def style_element_for(style)
81
- @options[:inline_style_mapping].value_of!(style)
82
+ Array(@options[:inline_style_mapping].value_of!(style))
82
83
  end
83
84
 
84
85
  def try_apply_entity_to(draftjs, char_range)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DraftjsHtml
4
- VERSION = "0.20.0"
4
+ VERSION = "0.22.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.20.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TJ Taylor
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-17 00:00:00.000000000 Z
11
+ date: 2022-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -68,10 +68,10 @@ files:
68
68
  - lib/draftjs_html/draftjs/raw_builder.rb
69
69
  - lib/draftjs_html/draftjs/to_raw.rb
70
70
  - lib/draftjs_html/from_html.rb
71
+ - lib/draftjs_html/from_html/char_list.rb
71
72
  - lib/draftjs_html/from_html/depth_stack.rb
72
73
  - lib/draftjs_html/from_html/elements.rb
73
74
  - lib/draftjs_html/from_html/pending_block.rb
74
- - lib/draftjs_html/from_html/style_stack.rb
75
75
  - lib/draftjs_html/html_defaults.rb
76
76
  - lib/draftjs_html/html_depth.rb
77
77
  - lib/draftjs_html/node.rb
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  - !ruby/object:Gem::Version
101
101
  version: '0'
102
102
  requirements: []
103
- rubygems_version: 3.1.6
103
+ rubygems_version: 3.3.26
104
104
  signing_key:
105
105
  specification_version: 4
106
106
  summary: A tool for converting DraftJS JSON to HTML (and back again)
@@ -1,50 +0,0 @@
1
- module DraftjsHtml
2
- class FromHtml < Nokogiri::XML::SAX::Document
3
- class StyleStack
4
- def initialize
5
- @stack = []
6
- end
7
-
8
- def clear_finished
9
- @stack.delete_if { !!_1[:finish] }
10
- end
11
-
12
- def each(&block)
13
- @stack.reverse_each.group_by { _1[:tagname] }.each do |_, descriptors|
14
- overlapping_ranges = find_overlapping_styles(descriptors)
15
- widest_descriptor = overlapping_ranges.max_by { (_1[:start].._1[:finish]).size }
16
-
17
- applicable_styles = descriptors - overlapping_ranges + [widest_descriptor].compact
18
- applicable_styles.each(&block)
19
- end
20
- end
21
-
22
- def track_start(tagname, current_character_offset)
23
- style = DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]
24
- @stack.unshift({ tagname: tagname, style: style, start: current_character_offset })
25
- end
26
-
27
- def track_end(tagname, current_character_offset)
28
- descriptor_index = @stack.find_index { _1[:tagname] == tagname && !_1[:finish] }
29
- descriptor = @stack[descriptor_index]
30
- descriptor[:finish] = current_character_offset
31
- end
32
-
33
- private
34
-
35
- def find_overlapping_styles(descriptors)
36
- descriptors.select do |candidate_a|
37
- candidate_range = candidate_a[:start]..candidate_a[:finish]
38
- (descriptors - [candidate_a]).any? do |other|
39
- other_range = other[:start]..other[:finish]
40
- range_overlaps?(candidate_range, other_range)
41
- end
42
- end
43
- end
44
-
45
- def range_overlaps?(candidate_range, other_range)
46
- other_range.begin == candidate_range.begin || candidate_range.cover?(other_range.begin) || other_range.cover?(candidate_range.begin)
47
- end
48
- end
49
- end
50
- end