richer_text 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/app/helpers/richer_text/tag_helper.rb +37 -0
- data/app/models/richer_text/json_text.rb +25 -2
- data/app/models/richer_text/rich_text.rb +6 -0
- data/lib/richer_text/content.rb +6 -0
- data/lib/richer_text/html_visitor.rb +33 -0
- data/lib/richer_text/node.rb +2 -2
- data/lib/richer_text/nodes/attachment_figure.rb +17 -0
- data/lib/richer_text/nodes/attachment_gallery.rb +6 -0
- data/lib/richer_text/nodes/mention.rb +30 -0
- data/lib/richer_text/version.rb +1 -1
- data/lib/richer_text.rb +4 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03e9732ed2320f56a2c30cbcd6af7614b6840b601aab70d790c96e6e8412babf
|
4
|
+
data.tar.gz: 4413b9240e46d0153dff22c7b4dd7161a555f572ada62b69159f0c267ae7b0b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39f20dfc3cecd2f74f4cc4d01b396d9678ceeed635ff4dc2a35bc62ba0dc8f6361f882dfe1df67b9286d63b1c86fbd6b7e73b729175e8fd3948435eb041a7ade
|
7
|
+
data.tar.gz: 4718871930d84edd4de392d9ea2d321f6a04782e5f0c774906e468e8c70ecac6ea9c78fb4ed92dcf7aa8598fea600dea3d13a117beef8287d5b709a8547d2318
|
@@ -17,16 +17,40 @@ module ActionView::Helpers
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
class Tags::RhinoEditor < Tags::Base
|
21
|
+
include Tags::Placeholderable
|
22
|
+
|
23
|
+
delegate :dom_id, to: ActionView::RecordIdentifier
|
24
|
+
|
25
|
+
def render
|
26
|
+
options = @options.stringify_keys
|
27
|
+
add_default_name_and_id(options)
|
28
|
+
options["input"] ||= dom_id(object, [options["id"], :rhino_text_input].compact.join("_")) if object
|
29
|
+
options["value"] = options.fetch("value") { value&.to_editor_format }
|
30
|
+
options["serializer"] = options.fetch("serializer") { "html"}
|
31
|
+
|
32
|
+
@template_object.rhino_text_area_tag(options.delete("name"), options["value"], options.except("value"))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
20
36
|
module FormHelper
|
21
37
|
def richer_text_area(object_name, method, options = {})
|
22
38
|
Tags::Editor.new(object_name, method, self, options).render
|
23
39
|
end
|
40
|
+
|
41
|
+
def rhino_text_area(object_name, method, options = {})
|
42
|
+
Tags::RhinoEditor.new(object_name, method, self, options).render
|
43
|
+
end
|
24
44
|
end
|
25
45
|
|
26
46
|
class FormBuilder
|
27
47
|
def richer_text_area(method, options = {})
|
28
48
|
@template.richer_text_area(@object_name, method, objectify_options(options))
|
29
49
|
end
|
50
|
+
|
51
|
+
def rhino_text_area(method, options = {})
|
52
|
+
@template.rhino_text_area(@object_name, method, objectify_options(options))
|
53
|
+
end
|
30
54
|
end
|
31
55
|
end
|
32
56
|
|
@@ -49,5 +73,18 @@ module RicherText
|
|
49
73
|
|
50
74
|
input_tag + editor_tag
|
51
75
|
end
|
76
|
+
|
77
|
+
def rhino_text_area_tag(name, value = nil, options = {})
|
78
|
+
options = options.symbolize_keys
|
79
|
+
options[:input] ||= "rhino_text_input_#{RicherText::TagHelper.id += 1}"
|
80
|
+
|
81
|
+
# So we can choose the serializer to use, e.g. "html" or "json"
|
82
|
+
options[:serializer] ||= "html"
|
83
|
+
|
84
|
+
input_tag = hidden_field_tag(name, value, id: options[:input])
|
85
|
+
editor_tag = tag("rhino-editor", { input: options[:input], serializer: "json", data: { "blob-url-template": rails_service_blob_url(":signed_id", ":filename"), "direct-upload-url": rails_direct_uploads_url }}.merge(options))
|
86
|
+
|
87
|
+
input_tag + editor_tag
|
88
|
+
end
|
52
89
|
end
|
53
90
|
end
|
@@ -7,8 +7,9 @@ module RicherText
|
|
7
7
|
serialize :body, JSON, default: DEFAULT_BODY.to_json
|
8
8
|
|
9
9
|
has_many_attached :images
|
10
|
+
has_many_attached :rhino_attachments # For handling attachments in the Rhino Editor
|
10
11
|
|
11
|
-
before_save :
|
12
|
+
before_save :update_attachments
|
12
13
|
|
13
14
|
def to_editor_format
|
14
15
|
body
|
@@ -18,18 +19,40 @@ module RicherText
|
|
18
19
|
RicherText.default_renderer.visit(document)
|
19
20
|
end
|
20
21
|
|
22
|
+
def mentionees
|
23
|
+
gids = mention_nodes.map(&:id).compact_blank
|
24
|
+
|
25
|
+
GlobalID::Locator.locate_many(gids).uniq # Only return unique records
|
26
|
+
end
|
27
|
+
|
21
28
|
private
|
22
29
|
|
23
|
-
def
|
30
|
+
def update_attachments
|
24
31
|
self.images = image_nodes.map(&:signed_id)
|
32
|
+
self.rhino_attachments = rhino_attachment_records.map(&:signed_id)
|
25
33
|
end
|
26
34
|
|
27
35
|
def image_nodes
|
28
36
|
find_nodes_of_type(RicherText::Nodes::Image).flatten.compact_blank
|
29
37
|
end
|
30
38
|
|
39
|
+
def mention_nodes
|
40
|
+
find_nodes_of_type(RicherText::Nodes::Mention).flatten.compact_blank
|
41
|
+
end
|
42
|
+
|
43
|
+
def rhino_attachment_nodes
|
44
|
+
find_nodes_of_type(RicherText::Nodes::AttachmentFigure).flatten.compact_blank
|
45
|
+
end
|
46
|
+
|
47
|
+
def rhino_attachment_records
|
48
|
+
sgids = rhino_attachment_nodes.map(&:sgid).compact_blank
|
49
|
+
GlobalID::Locator.locate_many_signed(sgids, for: "attachable")
|
50
|
+
end
|
51
|
+
|
31
52
|
def find_nodes_of_type(type, node = document)
|
32
53
|
if node.children.any?
|
54
|
+
return node if node.is_a?(type)
|
55
|
+
|
33
56
|
node.children.map { |child| find_nodes_of_type(type, child) }
|
34
57
|
else
|
35
58
|
node.is_a?(type) ? node : nil
|
data/lib/richer_text/content.rb
CHANGED
@@ -28,6 +28,12 @@ module RicherText
|
|
28
28
|
end.uniq
|
29
29
|
end
|
30
30
|
|
31
|
+
def mentionees_global_ids
|
32
|
+
global_ids = fragment.find_all("span[data-type=mention]").map do |span|
|
33
|
+
span.attributes["data-id"].value
|
34
|
+
end.uniq
|
35
|
+
end
|
36
|
+
|
31
37
|
def image_blobs
|
32
38
|
image_blob_ids.map { |id| ActiveStorage::Blob.find_signed(id) }
|
33
39
|
end
|
@@ -6,6 +6,18 @@ module RicherText
|
|
6
6
|
node.accept(self)
|
7
7
|
end
|
8
8
|
|
9
|
+
def visit_attachment_figure(node)
|
10
|
+
"<figure sgid=#{node.attrs["sgid"]}>
|
11
|
+
#{node_previewable?(node) ? "<img src=#{node.url} />" : "<a href=#{node.url} target='_blank'>"}
|
12
|
+
<figcaption class='attachment__caption'>#{visit_children(node).join}</figcaption>
|
13
|
+
#{node_previewable?(node) ? "" : "</a>"}
|
14
|
+
</figure>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_attachment_gallery(node)
|
18
|
+
"<div class='attachment-gallery'>#{visit_children(node).join}</div>"
|
19
|
+
end
|
20
|
+
|
9
21
|
def visit_blockquote(node)
|
10
22
|
"<blockquote>#{visit_children(node).join}</blockquote>"
|
11
23
|
end
|
@@ -30,6 +42,17 @@ module RicherText
|
|
30
42
|
"<div class='richer-text'>#{visit_children(node).join("\n")}</div>".html_safe
|
31
43
|
end
|
32
44
|
|
45
|
+
def visit_mention(node, marks)
|
46
|
+
if marks.any?
|
47
|
+
content_tag(marks[0].tag, visit_mention(node, marks[1..]), marks[0].attrs)
|
48
|
+
else
|
49
|
+
tag.span(
|
50
|
+
tag.img(src: node.avatar_url, class: "richer-text--mention-img") +
|
51
|
+
tag.span(node.name, class: "richer-text--mention-label"),
|
52
|
+
class: "richer-text--mention")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
33
56
|
def visit_paragraph(node)
|
34
57
|
"<p style='#{node.style}'>#{visit_children(node).join}</p>"
|
35
58
|
end
|
@@ -81,5 +104,15 @@ module RicherText
|
|
81
104
|
def visit_table_header(node)
|
82
105
|
"<th>#{visit_children(node).join}</th>"
|
83
106
|
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def previewable_regex
|
111
|
+
/^image(\/(gif|png|jpe?g)|$)/
|
112
|
+
end
|
113
|
+
|
114
|
+
def node_previewable?(node)
|
115
|
+
node.attrs["contentType"].match(previewable_regex)
|
116
|
+
end
|
84
117
|
end
|
85
118
|
end
|
data/lib/richer_text/node.rb
CHANGED
@@ -8,8 +8,8 @@ module RicherText
|
|
8
8
|
|
9
9
|
def self.build(json)
|
10
10
|
node = json.is_a?(String) ? JSON.parse(json) : json
|
11
|
-
klass = "RicherText::Nodes::#{node["type"].underscore.classify}".
|
12
|
-
klass.new(node)
|
11
|
+
klass = "RicherText::Nodes::#{node["type"].underscore.classify}".constantize
|
12
|
+
klass.new(node) if klass
|
13
13
|
end
|
14
14
|
|
15
15
|
def initialize(json)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RicherText
|
2
|
+
module Nodes
|
3
|
+
class AttachmentFigure < ::RicherText::Node
|
4
|
+
def previewable?
|
5
|
+
@attrs["previewable"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def url
|
9
|
+
@attrs["url"].presence || @attrs["src"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def sgid
|
13
|
+
@attrs["sgid"]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RicherText
|
2
|
+
module Nodes
|
3
|
+
class Mention < ::RicherText::Node
|
4
|
+
def initialize(json)
|
5
|
+
@marks = json.fetch("marks", []).map { |mark| RicherText::Mark.new(mark) }
|
6
|
+
super(json)
|
7
|
+
end
|
8
|
+
|
9
|
+
def accept(visitor)
|
10
|
+
visitor.visit_mention(self, @marks)
|
11
|
+
end
|
12
|
+
|
13
|
+
def user
|
14
|
+
@user ||= GlobalID::Locator.locate(@attrs["id"])
|
15
|
+
end
|
16
|
+
|
17
|
+
def id
|
18
|
+
@attrs["id"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
@user.respond_to?(:name) ? @user.name : @attrs["label"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def avatar_url
|
26
|
+
@user.respond_to?(:avatar_url) ? @user.avatar_url : @attrs["avatarUrl"]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/richer_text/version.rb
CHANGED
data/lib/richer_text.rb
CHANGED
@@ -21,6 +21,8 @@ module RicherText
|
|
21
21
|
module Nodes
|
22
22
|
extend ActiveSupport::Autoload
|
23
23
|
|
24
|
+
autoload :AttachmentFigure # For Rhino Editor support
|
25
|
+
autoload :AttachmentGallery # For Rhino Editor support
|
24
26
|
autoload :Blockquote
|
25
27
|
autoload :BulletList
|
26
28
|
autoload :Callout
|
@@ -31,8 +33,9 @@ module RicherText
|
|
31
33
|
autoload :HorizontalRule
|
32
34
|
autoload :Image
|
33
35
|
autoload :ListItem
|
34
|
-
autoload :
|
36
|
+
autoload :Mention
|
35
37
|
autoload :OrderedList
|
38
|
+
autoload :Paragraph
|
36
39
|
autoload :Text
|
37
40
|
autoload :Table
|
38
41
|
autoload :TableCell
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: richer_text
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrea Fomera
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -59,6 +59,8 @@ files:
|
|
59
59
|
- lib/richer_text/html_visitor.rb
|
60
60
|
- lib/richer_text/mark.rb
|
61
61
|
- lib/richer_text/node.rb
|
62
|
+
- lib/richer_text/nodes/attachment_figure.rb
|
63
|
+
- lib/richer_text/nodes/attachment_gallery.rb
|
62
64
|
- lib/richer_text/nodes/blockquote.rb
|
63
65
|
- lib/richer_text/nodes/bullet_list.rb
|
64
66
|
- lib/richer_text/nodes/callout.rb
|
@@ -69,6 +71,7 @@ files:
|
|
69
71
|
- lib/richer_text/nodes/horizontal_rule.rb
|
70
72
|
- lib/richer_text/nodes/image.rb
|
71
73
|
- lib/richer_text/nodes/list_item.rb
|
74
|
+
- lib/richer_text/nodes/mention.rb
|
72
75
|
- lib/richer_text/nodes/ordered_list.rb
|
73
76
|
- lib/richer_text/nodes/paragraph.rb
|
74
77
|
- lib/richer_text/nodes/table.rb
|