tiptap-ruby 0.1.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +89 -0
- data/lib/tip_tap/document.rb +47 -0
- data/lib/tip_tap/has_content.rb +49 -0
- data/lib/tip_tap/html_renderable.rb +60 -0
- data/lib/tip_tap/json_renderable.rb +44 -0
- data/lib/tip_tap/node.rb +18 -0
- data/lib/tip_tap/nodes/bullet_list.rb +19 -0
- data/lib/tip_tap/nodes/hard_break.rb +19 -0
- data/lib/tip_tap/nodes/heading.rb +20 -0
- data/lib/tip_tap/nodes/horizontal_rule.rb +19 -0
- data/lib/tip_tap/nodes/image.rb +23 -0
- data/lib/tip_tap/nodes/list_item.rb +19 -0
- data/lib/tip_tap/nodes/ordered_list.rb +23 -0
- data/lib/tip_tap/nodes/paragraph.rb +16 -0
- data/lib/tip_tap/nodes/task_item.rb +23 -0
- data/lib/tip_tap/nodes/task_list.rb +19 -0
- data/lib/tip_tap/nodes/text.rb +75 -0
- data/lib/tip_tap/plain_text_renderable.rb +10 -0
- data/lib/tip_tap/registry.rb +27 -0
- data/lib/tip_tap/version.rb +5 -0
- data/lib/tip_tap.rb +24 -0
- data/lib/tiptap.rb +3 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2347235752700026d263aa5a0616d89f99014c42da52edbf92c30374eb2cfded
|
4
|
+
data.tar.gz: d97eefd5023412988280b6edf6741186f310452ab657a02c62419296de680334
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79f61b79665a0a0d48cc69322c50b24d05a3b330c6f2a6dd8242f31dc628d8f9e1d69db06024b7bb642290c44a001df13379fcc45872a6c0f0f3e28df0bc4a3f
|
7
|
+
data.tar.gz: 4b5ab21878bccf69ccdc8dfdc3fe04bbc5e3a91a65140b6d2b1759bce3ed13a6169fb47d2697415cd5d27261fa27c11f1d40c2e0387a1f0e30cf38fc296244a0
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Chad Wilken
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Tiptap
|
2
|
+
|
3
|
+
A gem for parsing, generating, and rendering TipTap Documents and Nodes using Ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
8
|
+
|
9
|
+
$ bundle add tiptap-ruby
|
10
|
+
|
11
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
12
|
+
|
13
|
+
$ gem install tiptap-ruby
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
### Parsing a TipTap Document
|
18
|
+
|
19
|
+
You can parse a TipTap Document so that you can interact with it to do things such as add content (Nodes) or render it as HTML, JSON, or plain text:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
document = TipTap::Document.from_json(tiptap_json)
|
23
|
+
```
|
24
|
+
|
25
|
+
### Generate a New Document
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
document = TipTap::Document.new
|
29
|
+
```
|
30
|
+
|
31
|
+
You can also pass a block and the new Document will be yielded to the block.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
TipTap::Document.new do |document|
|
35
|
+
# Do something with document
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
### Add Content to the Document
|
40
|
+
|
41
|
+
Now that you have a Document you can add content to it.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
document.heading(level: 1) do |heading|
|
45
|
+
heading.text("My Import Document", marks: [{type: "italic"}])
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Until the gem implements all of the node types and the documentation is complete, refer to the `Document` class to see the nodes that can be appended.
|
50
|
+
|
51
|
+
### Generate Output
|
52
|
+
|
53
|
+
Once you have a Document with some content you can render it to HTML, JSON, and plain text.
|
54
|
+
|
55
|
+
#### JSON
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
document.to_json # => { type: 'doc', content: […nodes]}
|
59
|
+
```
|
60
|
+
|
61
|
+
### HTML
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
document.to_html # => <div class="tiptap-document"><h1><em>My Important Document</em></h1></div>
|
65
|
+
```
|
66
|
+
|
67
|
+
### Plain Text
|
68
|
+
|
69
|
+
Rendering to plain text is useful if you want to search the contents of your TipTap content.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
document.to_plain_text # => My Important Document
|
73
|
+
```
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
To install this gem onto your local machine, run `bundle install`.
|
80
|
+
|
81
|
+
To release a new version, update the version number in `version.rb`, and then run `bin/release` or `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/chadwilken/tiptap-ruby.
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/html_renderable"
|
4
|
+
require "tip_tap/json_renderable"
|
5
|
+
require "tip_tap/plain_text_renderable"
|
6
|
+
require "tip_tap/has_content"
|
7
|
+
|
8
|
+
# This is the class that all child nodes will be added to.
|
9
|
+
# This is the root object for TipTap.
|
10
|
+
module TipTap
|
11
|
+
class Document
|
12
|
+
include JsonRenderable
|
13
|
+
include HtmlRenderable
|
14
|
+
include PlainTextRenderable
|
15
|
+
include HasContent
|
16
|
+
|
17
|
+
self.type_name = "doc"
|
18
|
+
self.html_tag = :div
|
19
|
+
self.html_class_name = "tiptap-document"
|
20
|
+
|
21
|
+
def heading(level: 1, &block)
|
22
|
+
raise ArgumentError, "Block required" if block.nil?
|
23
|
+
|
24
|
+
add_content(Nodes::Heading.new(level: level, &block))
|
25
|
+
end
|
26
|
+
|
27
|
+
def paragraph(&block)
|
28
|
+
add_content(Nodes::Paragraph.new(&block))
|
29
|
+
end
|
30
|
+
|
31
|
+
def task_list(&block)
|
32
|
+
raise ArgumentError, "Block required" if block.nil?
|
33
|
+
|
34
|
+
add_content(Nodes::TaskList.new(&block))
|
35
|
+
end
|
36
|
+
|
37
|
+
def bullet_list(&block)
|
38
|
+
raise ArgumentError, "Block required" if block.nil?
|
39
|
+
|
40
|
+
add_content(Nodes::BulletList.new(&block))
|
41
|
+
end
|
42
|
+
|
43
|
+
def image(src:)
|
44
|
+
add_content(Nodes::Image.new(src: src))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module HasContent
|
7
|
+
attr_reader :attrs, :content
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create a new document or node, optionally passing in attributes (attrs) and nodes
|
14
|
+
# @param nodes [Array] An array of nodes to add to this node
|
15
|
+
# @param attributes [Hash] A hash of attributes to add to this node e.g. { 'level' => 1 }
|
16
|
+
def initialize(content = [], **attributes)
|
17
|
+
# This will convert the attrs key to camelcase for example :image_id is converted into 'imageId'
|
18
|
+
@attrs = Hash(attributes).deep_transform_keys { |key| key.to_s.camelcase(:lower) }
|
19
|
+
@content = content
|
20
|
+
yield self if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_node(node_type)
|
24
|
+
content.find { |child| child.is_a?(node_type) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_content(node)
|
28
|
+
@content << node
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
# Create a new instance from a TipTap JSON object.
|
33
|
+
# All nodes are recursively parsed and converted to Ruby objects
|
34
|
+
# All nodes must be registered in the registry.
|
35
|
+
# @param json [Hash] The JSON object to parse
|
36
|
+
def from_json(json)
|
37
|
+
return new if json.nil?
|
38
|
+
|
39
|
+
json.deep_stringify_keys!
|
40
|
+
|
41
|
+
content = Array(json["content"]).map do |node|
|
42
|
+
TipTap.node_for(node["type"]).from_json(node)
|
43
|
+
end
|
44
|
+
|
45
|
+
new(content, **Hash(json["attrs"]))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module HtmlRenderable
|
7
|
+
include ActionView::Helpers::TextHelper
|
8
|
+
include ActionView::Helpers::AssetTagHelper
|
9
|
+
|
10
|
+
# ActionView::Helpers::TagHelper requires output_buffer accessor
|
11
|
+
# This is included by ActionView::Helpers::TextHelper
|
12
|
+
attr_accessor :output_buffer
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.extend(ClassMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def html_tag=(name_or_proc)
|
20
|
+
@html_tag = name_or_proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def html_tag
|
24
|
+
@html_tag
|
25
|
+
end
|
26
|
+
|
27
|
+
def html_class_name=(name_or_proc)
|
28
|
+
@html_class_name = name_or_proc
|
29
|
+
end
|
30
|
+
|
31
|
+
def html_class_name
|
32
|
+
@html_class_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def html_tag
|
37
|
+
tag = self.class.html_tag
|
38
|
+
case tag
|
39
|
+
when Proc
|
40
|
+
instance_eval(&tag)
|
41
|
+
else
|
42
|
+
tag
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def html_class_name
|
47
|
+
classes = self.class.html_class_name
|
48
|
+
case classes
|
49
|
+
when Proc
|
50
|
+
instance_eval(&classes)
|
51
|
+
else
|
52
|
+
classes
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_html
|
57
|
+
content_tag(html_tag, safe_join(content.map(&:to_html)), class: html_class_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/registry"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module JsonRenderable
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def type_name=(type_name)
|
13
|
+
@type_name = type_name
|
14
|
+
Registry.register(type_name, self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def type_name
|
18
|
+
@type_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_name
|
23
|
+
self.class.type_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def include_empty_content_in_json?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generate a JSON object that is useable by the editor
|
31
|
+
def to_json
|
32
|
+
json = {type: type_name}
|
33
|
+
json = json.merge(content: content.map(&:to_json)) if should_include_content?
|
34
|
+
json = json.merge(attrs: attrs.deep_symbolize_keys) if attrs.present?
|
35
|
+
json
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def should_include_content?
|
41
|
+
content.present? || (content.empty? && include_empty_content_in_json?)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/tip_tap/node.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/html_renderable"
|
4
|
+
require "tip_tap/json_renderable"
|
5
|
+
require "tip_tap/plain_text_renderable"
|
6
|
+
require "tip_tap/has_content"
|
7
|
+
|
8
|
+
# This is the base class for all TipTap nodes.
|
9
|
+
# It provides some basic functionality that all nodes need such as
|
10
|
+
# converting to HTML, JSON, and plain text
|
11
|
+
module TipTap
|
12
|
+
class Node
|
13
|
+
include HtmlRenderable
|
14
|
+
include JsonRenderable
|
15
|
+
include PlainTextRenderable
|
16
|
+
include HasContent
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class BulletList < Node
|
8
|
+
self.type_name = "bulletList"
|
9
|
+
self.html_tag = :ul
|
10
|
+
self.html_class_name = "bullet-list"
|
11
|
+
|
12
|
+
def list_item(&block)
|
13
|
+
raise ArgumentError, "Block required" if block.nil?
|
14
|
+
|
15
|
+
add_content(ListItem.new(&block))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class HardBreak < Node
|
8
|
+
self.type_name = "hardBreak"
|
9
|
+
|
10
|
+
def include_empty_content_in_json?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_html
|
15
|
+
tag.br
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class Heading < Node
|
8
|
+
self.type_name = "heading"
|
9
|
+
self.html_tag = proc { "h#{level}" }
|
10
|
+
|
11
|
+
def text(text, marks: [])
|
12
|
+
add_content(Text.new(text, marks: marks))
|
13
|
+
end
|
14
|
+
|
15
|
+
def level
|
16
|
+
attrs["level"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class HorizontalRule < Node
|
8
|
+
self.type_name = "horizontalRule"
|
9
|
+
|
10
|
+
def include_empty_content_in_json?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_html
|
15
|
+
tag.hr
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class Image < Node
|
8
|
+
self.type_name = "image"
|
9
|
+
|
10
|
+
def include_empty_content_in_json?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_html
|
15
|
+
image_tag(src)
|
16
|
+
end
|
17
|
+
|
18
|
+
def src
|
19
|
+
attrs["src"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class ListItem < Node
|
8
|
+
self.type_name = "listItem"
|
9
|
+
self.html_tag = :li
|
10
|
+
self.html_class_name = "list-item"
|
11
|
+
|
12
|
+
def paragraph(&block)
|
13
|
+
raise ArgumentError, "Block required" if block.nil?
|
14
|
+
|
15
|
+
add_content(Paragraph.new(&block))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class OrderedList < Node
|
8
|
+
self.type_name = "orderedList"
|
9
|
+
self.html_tag = :ol
|
10
|
+
self.html_class_name = "ordered-list"
|
11
|
+
|
12
|
+
def list_item(&block)
|
13
|
+
raise ArgumentError, "Block required" if block.nil?
|
14
|
+
|
15
|
+
add_content(ListItem.new(&block))
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
attrs["start"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class Paragraph < Node
|
8
|
+
self.type_name = "paragraph"
|
9
|
+
self.html_tag = :p
|
10
|
+
|
11
|
+
def text(text, marks: [])
|
12
|
+
add_content(Text.new(text, marks: marks))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class TaskItem < Node
|
8
|
+
self.type_name = "taskItem"
|
9
|
+
self.html_tag = :li
|
10
|
+
self.html_class_name = proc { class_names("task-item", {checked: checked?}) }
|
11
|
+
|
12
|
+
def paragraph(&block)
|
13
|
+
raise ArgumentError, "Block required" if block.nil?
|
14
|
+
|
15
|
+
add_content(Paragraph.new(&block))
|
16
|
+
end
|
17
|
+
|
18
|
+
def checked?
|
19
|
+
attrs["checked"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/node"
|
4
|
+
|
5
|
+
module TipTap
|
6
|
+
module Nodes
|
7
|
+
class TaskList < Node
|
8
|
+
self.type_name = "taskList"
|
9
|
+
self.html_tag = :ul
|
10
|
+
self.html_class_name = "task-list"
|
11
|
+
|
12
|
+
def task_item(checked: false, &block)
|
13
|
+
raise ArgumentError, "Block required" if block.nil?
|
14
|
+
|
15
|
+
add_content(TaskItem.new(checked: checked, &block))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tip_tap/json_renderable"
|
4
|
+
require "tip_tap/html_renderable"
|
5
|
+
|
6
|
+
module TipTap
|
7
|
+
module Nodes
|
8
|
+
class Text
|
9
|
+
include JsonRenderable
|
10
|
+
include HtmlRenderable
|
11
|
+
|
12
|
+
attr_reader :text, :marks
|
13
|
+
|
14
|
+
self.type_name = "text"
|
15
|
+
|
16
|
+
def initialize(text, marks: [])
|
17
|
+
@text = text
|
18
|
+
@marks = marks.map(&:deep_stringify_keys)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_json(json)
|
22
|
+
json.deep_stringify_keys!
|
23
|
+
|
24
|
+
new(json["text"], marks: Array(json["marks"]))
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_json
|
28
|
+
{type: type_name, text: text, marks: marks.map(&:deep_symbolize_keys)}.compact_blank
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_html
|
32
|
+
value = text
|
33
|
+
value = content_tag(:u, value) if underline?
|
34
|
+
value = content_tag(:em, value) if italic?
|
35
|
+
value = content_tag(:strong, value) if bold?
|
36
|
+
value = content_tag(:a, value, href: link_href, target: link_target) if link?
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_plain_text
|
41
|
+
text
|
42
|
+
end
|
43
|
+
|
44
|
+
def italic?
|
45
|
+
has_mark_with_type?("italic")
|
46
|
+
end
|
47
|
+
|
48
|
+
def bold?
|
49
|
+
has_mark_with_type?("bold")
|
50
|
+
end
|
51
|
+
|
52
|
+
def underline?
|
53
|
+
has_mark_with_type?("underline")
|
54
|
+
end
|
55
|
+
|
56
|
+
def link?
|
57
|
+
has_mark_with_type?("link")
|
58
|
+
end
|
59
|
+
|
60
|
+
def link_href
|
61
|
+
marks.find { |mark| mark["type"] == "link" }&.dig("attrs", "href")
|
62
|
+
end
|
63
|
+
|
64
|
+
def link_target
|
65
|
+
marks.find { |mark| mark["type"] == "link" }&.dig("attrs", "target")
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def has_mark_with_type?(type)
|
71
|
+
marks.any? { |mark| mark["type"] == type }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is the registry for all the nodes that TipTap knows about.
|
4
|
+
# It's a simple hash that maps TipTap JS node names to Ruby classes.
|
5
|
+
# Registering a new node is as simple as:
|
6
|
+
# TipTap::Registry.register('myNode', MyNode)
|
7
|
+
module TipTap
|
8
|
+
class Registry
|
9
|
+
MissingNodeError = Class.new(StandardError)
|
10
|
+
|
11
|
+
def self.register(name, klass)
|
12
|
+
registry[name.to_s] = klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.node_for(name)
|
16
|
+
registry.fetch(name.to_s) { raise MissingNodeError.new("Unknown node type: #{name}") }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.clear
|
20
|
+
@registry = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.registry
|
24
|
+
@registry ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/tip_tap.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "tip_tap/version"
|
4
|
+
require "tip_tap/registry"
|
5
|
+
require "tip_tap/document"
|
6
|
+
require "tip_tap/nodes/bullet_list"
|
7
|
+
require "tip_tap/nodes/hard_break"
|
8
|
+
require "tip_tap/nodes/heading"
|
9
|
+
require "tip_tap/nodes/horizontal_rule"
|
10
|
+
require "tip_tap/nodes/list_item"
|
11
|
+
require "tip_tap/nodes/ordered_list"
|
12
|
+
require "tip_tap/nodes/paragraph"
|
13
|
+
require "tip_tap/nodes/task_item"
|
14
|
+
require "tip_tap/nodes/task_list"
|
15
|
+
require "tip_tap/nodes/text"
|
16
|
+
require "tip_tap/nodes/image"
|
17
|
+
|
18
|
+
module TipTap
|
19
|
+
class Error < StandardError; end
|
20
|
+
|
21
|
+
def self.node_for(name)
|
22
|
+
Registry.node_for(name)
|
23
|
+
end
|
24
|
+
end
|
data/lib/tiptap.rb
ADDED
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tiptap-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chad Wilken
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionview
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.0'
|
41
|
+
description: A gem for parsing, generating, and rendering TipTap Documents and Nodes
|
42
|
+
using Ruby.
|
43
|
+
email:
|
44
|
+
- chad.wilken@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- CHANGELOG.md
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- lib/tip_tap.rb
|
53
|
+
- lib/tip_tap/document.rb
|
54
|
+
- lib/tip_tap/has_content.rb
|
55
|
+
- lib/tip_tap/html_renderable.rb
|
56
|
+
- lib/tip_tap/json_renderable.rb
|
57
|
+
- lib/tip_tap/node.rb
|
58
|
+
- lib/tip_tap/nodes/bullet_list.rb
|
59
|
+
- lib/tip_tap/nodes/hard_break.rb
|
60
|
+
- lib/tip_tap/nodes/heading.rb
|
61
|
+
- lib/tip_tap/nodes/horizontal_rule.rb
|
62
|
+
- lib/tip_tap/nodes/image.rb
|
63
|
+
- lib/tip_tap/nodes/list_item.rb
|
64
|
+
- lib/tip_tap/nodes/ordered_list.rb
|
65
|
+
- lib/tip_tap/nodes/paragraph.rb
|
66
|
+
- lib/tip_tap/nodes/task_item.rb
|
67
|
+
- lib/tip_tap/nodes/task_list.rb
|
68
|
+
- lib/tip_tap/nodes/text.rb
|
69
|
+
- lib/tip_tap/plain_text_renderable.rb
|
70
|
+
- lib/tip_tap/registry.rb
|
71
|
+
- lib/tip_tap/version.rb
|
72
|
+
- lib/tiptap.rb
|
73
|
+
homepage: https://github.com/chadwilken/tiptap-ruby
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
homepage_uri: https://github.com/chadwilken/tiptap-ruby
|
78
|
+
source_code_uri: https://github.com/chadwilken/tiptap-ruby
|
79
|
+
changelog_uri: https://github.com/chadwilken/tiptap-ruby/blob/master/CHANGELOG.md
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.7.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.4.6
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Parse, generate and render TipTap documents in Ruby.
|
99
|
+
test_files: []
|