prosereflect 0.1.0 → 0.1.1
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/.rubocop_todo.yml +23 -4
- data/README.adoc +193 -12
- data/lib/prosereflect/attribute/base.rb +34 -0
- data/lib/prosereflect/attribute/bold.rb +20 -0
- data/lib/prosereflect/attribute/href.rb +24 -0
- data/lib/prosereflect/attribute/id.rb +24 -0
- data/lib/prosereflect/attribute.rb +13 -0
- data/lib/prosereflect/blockquote.rb +85 -0
- data/lib/prosereflect/bullet_list.rb +83 -0
- data/lib/prosereflect/code_block.rb +135 -0
- data/lib/prosereflect/code_block_wrapper.rb +66 -0
- data/lib/prosereflect/document.rb +99 -24
- data/lib/prosereflect/hard_break.rb +11 -9
- data/lib/prosereflect/heading.rb +64 -0
- data/lib/prosereflect/horizontal_rule.rb +70 -0
- data/lib/prosereflect/image.rb +126 -0
- data/lib/prosereflect/input/html.rb +505 -0
- data/lib/prosereflect/list_item.rb +65 -0
- data/lib/prosereflect/mark/base.rb +49 -0
- data/lib/prosereflect/mark/bold.rb +15 -0
- data/lib/prosereflect/mark/code.rb +14 -0
- data/lib/prosereflect/mark/italic.rb +15 -0
- data/lib/prosereflect/mark/link.rb +18 -0
- data/lib/prosereflect/mark/strike.rb +15 -0
- data/lib/prosereflect/mark/subscript.rb +15 -0
- data/lib/prosereflect/mark/superscript.rb +15 -0
- data/lib/prosereflect/mark/underline.rb +15 -0
- data/lib/prosereflect/mark.rb +11 -0
- data/lib/prosereflect/node.rb +181 -32
- data/lib/prosereflect/ordered_list.rb +85 -0
- data/lib/prosereflect/output/html.rb +374 -0
- data/lib/prosereflect/paragraph.rb +26 -15
- data/lib/prosereflect/parser.rb +111 -24
- data/lib/prosereflect/table.rb +40 -9
- data/lib/prosereflect/table_cell.rb +33 -8
- data/lib/prosereflect/table_header.rb +92 -0
- data/lib/prosereflect/table_row.rb +31 -8
- data/lib/prosereflect/text.rb +13 -17
- data/lib/prosereflect/user.rb +63 -0
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +6 -0
- data/prosereflect.gemspec +1 -0
- data/spec/prosereflect/document_spec.rb +436 -36
- data/spec/prosereflect/hard_break_spec.rb +218 -22
- data/spec/prosereflect/input/html_spec.rb +797 -0
- data/spec/prosereflect/node_spec.rb +258 -89
- data/spec/prosereflect/output/html_spec.rb +369 -0
- data/spec/prosereflect/paragraph_spec.rb +424 -49
- data/spec/prosereflect/parser_spec.rb +304 -91
- data/spec/prosereflect/table_cell_spec.rb +268 -57
- data/spec/prosereflect/table_row_spec.rb +210 -40
- data/spec/prosereflect/table_spec.rb +392 -61
- data/spec/prosereflect/text_spec.rb +206 -48
- data/spec/prosereflect/user_spec.rb +73 -0
- data/spec/prosereflect_spec.rb +5 -0
- data/spec/support/shared_examples.rb +44 -15
- metadata +47 -3
- data/debug_loading.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b84ac8ec1c9f76d0998f9b1be06b156f7ea6788f9c1d82fd2b2a34ef328263e0
|
4
|
+
data.tar.gz: 82c2549e3ae7a86cc6295e3b2230899638489743e4fa6d040a2a3f7fddbf8ffb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7f45b590785499274eb6112a961e131af7af6a5c679a388eaf3bd01805a731f572fbf7d5efa938f5466e359e2c3dc81953816164ac3a3c0585ab9e98b97a220
|
7
|
+
data.tar.gz: dd99eed85e651b903869b53a09866a550e71b5fd29efbf8fa380b6f35bee33c2189675ca1897d10f11400975a45dbb13d798fef916a466b61181a9046fee5ff2
|
data/.rubocop_todo.yml
CHANGED
@@ -76,22 +76,31 @@ Metrics/AbcSize:
|
|
76
76
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
77
77
|
# AllowedMethods: refine
|
78
78
|
Metrics/BlockLength:
|
79
|
-
|
79
|
+
Exclude:
|
80
|
+
- 'spec/prosereflect/input/html_spec.rb'
|
81
|
+
Max: 500
|
82
|
+
|
83
|
+
# Offense count: 3
|
84
|
+
Metrics/ClassLength:
|
85
|
+
Exclude:
|
86
|
+
- 'lib/prosereflect/input/html.rb'
|
87
|
+
- 'lib/prosereflect/output/html.rb'
|
88
|
+
- 'lib/prosereflect/node.rb'
|
80
89
|
|
81
90
|
# Offense count: 4
|
82
91
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
83
92
|
Metrics/CyclomaticComplexity:
|
84
|
-
Max:
|
93
|
+
Max: 50
|
85
94
|
|
86
95
|
# Offense count: 6
|
87
96
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
88
97
|
Metrics/MethodLength:
|
89
|
-
Max:
|
98
|
+
Max: 90
|
90
99
|
|
91
100
|
# Offense count: 3
|
92
101
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
93
102
|
Metrics/PerceivedComplexity:
|
94
|
-
Max:
|
103
|
+
Max: 35
|
95
104
|
|
96
105
|
# Offense count: 9
|
97
106
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
@@ -129,6 +138,16 @@ Style/Documentation:
|
|
129
138
|
- 'lib/prosereflect/table_cell.rb'
|
130
139
|
- 'lib/prosereflect/table_row.rb'
|
131
140
|
- 'lib/prosereflect/text.rb'
|
141
|
+
- 'lib/prosereflect/heading.rb'
|
142
|
+
- 'lib/prosereflect/attribute.rb'
|
143
|
+
- 'lib/prosereflect/attribute/base.rb'
|
144
|
+
- 'lib/prosereflect/attribute/bold.rb'
|
145
|
+
- 'lib/prosereflect/attribute/href.rb'
|
146
|
+
- 'lib/prosereflect/attribute/id.rb'
|
147
|
+
- 'lib/prosereflect/input/html.rb'
|
148
|
+
- 'lib/prosereflect/output/html.rb'
|
149
|
+
- 'lib/prosereflect/mark.rb'
|
150
|
+
- 'lib/prosereflect/mark/base.rb'
|
132
151
|
|
133
152
|
# Offense count: 1
|
134
153
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
data/README.adoc
CHANGED
@@ -80,7 +80,7 @@ tables = document.tables
|
|
80
80
|
paragraphs = document.paragraphs
|
81
81
|
|
82
82
|
# Access the first table
|
83
|
-
first_table = document.
|
83
|
+
first_table = document.find_first('table')
|
84
84
|
|
85
85
|
# Access header row and data rows in a table
|
86
86
|
header = first_table.header_row
|
@@ -119,7 +119,97 @@ tables = document.find_all('table')
|
|
119
119
|
text_nodes = document.find_all('text')
|
120
120
|
|
121
121
|
# Find child nodes of a specific type
|
122
|
-
table_cells = table.find_children(
|
122
|
+
table_cells = table.find_children(TableCell)
|
123
|
+
----
|
124
|
+
|
125
|
+
=== HTML Conversion
|
126
|
+
|
127
|
+
The gem provides functionality to convert between HTML and ProseMirror document models.
|
128
|
+
|
129
|
+
==== From HTML
|
130
|
+
|
131
|
+
[source,ruby]
|
132
|
+
----
|
133
|
+
require 'prosereflect'
|
134
|
+
|
135
|
+
# Parse from HTML string
|
136
|
+
html_content = '<p>This is a <strong>bold</strong> text in a paragraph.</p>'
|
137
|
+
document = Prosereflect::Input::Html.parse(html_content)
|
138
|
+
|
139
|
+
# Access the document structure
|
140
|
+
paragraph = document.paragraphs.first
|
141
|
+
text_content = paragraph.text_content # "This is a bold text in a paragraph."
|
142
|
+
----
|
143
|
+
|
144
|
+
==== User Mentions
|
145
|
+
|
146
|
+
The gem supports user mentions in documents, which can be useful for social features or collaborative editing.
|
147
|
+
|
148
|
+
[source,ruby]
|
149
|
+
----
|
150
|
+
# Create a document with user mentions
|
151
|
+
document = Prosereflect::Document.create
|
152
|
+
paragraph = document.add_paragraph('Hello ')
|
153
|
+
|
154
|
+
# Add a user mention
|
155
|
+
user = Prosereflect::User.new
|
156
|
+
user.id = '123'
|
157
|
+
paragraph.add_child(user)
|
158
|
+
|
159
|
+
paragraph.add_text('!')
|
160
|
+
|
161
|
+
# Convert to HTML
|
162
|
+
html = Prosereflect::Output::Html.convert(document)
|
163
|
+
# => "<p>Hello <user-mention data-id=\"123\"></user-mention>!</p>"
|
164
|
+
|
165
|
+
# Parse HTML with user mentions
|
166
|
+
html_content = '<p>Hello <user-mention data-id="123"></user-mention>!</p>'
|
167
|
+
document = Prosereflect::Input::Html.parse(html_content)
|
168
|
+
|
169
|
+
# Access user mentions
|
170
|
+
user_mentions = document.find_all('user')
|
171
|
+
first_user = user_mentions.first
|
172
|
+
user_id = first_user.id # => "123"
|
173
|
+
----
|
174
|
+
|
175
|
+
User mentions are represented as `<user-mention>` elements in HTML with a `data-id` attribute containing the user's identifier. When parsing HTML, these elements are converted to `User` nodes in the document model.
|
176
|
+
|
177
|
+
Common use cases:
|
178
|
+
- Mentioning users in comments or messages
|
179
|
+
- Tagging users in collaborative documents
|
180
|
+
- Tracking user references in content
|
181
|
+
|
182
|
+
==== To HTML
|
183
|
+
|
184
|
+
[source,ruby]
|
185
|
+
----
|
186
|
+
require 'prosereflect'
|
187
|
+
|
188
|
+
# Create a document
|
189
|
+
document = Prosereflect::Document.create
|
190
|
+
paragraph = document.add_paragraph('Plain text')
|
191
|
+
paragraph.add_text(' with bold', [Prosereflect::Mark::Bold.new])
|
192
|
+
|
193
|
+
# Convert to HTML
|
194
|
+
html = Prosereflect::Output::Html.convert(document)
|
195
|
+
# => "<html><body><p>Plain text<strong> with bold</strong></p></body></html>"
|
196
|
+
----
|
197
|
+
|
198
|
+
==== Round-trip Conversion
|
199
|
+
|
200
|
+
[source,ruby]
|
201
|
+
----
|
202
|
+
# Start with HTML
|
203
|
+
original_html = '<p>This is <em>styled</em> text.</p>'
|
204
|
+
|
205
|
+
# Convert to document model
|
206
|
+
document = Prosereflect::Input::Html.parse(original_html)
|
207
|
+
|
208
|
+
# Modify the document if needed
|
209
|
+
document.paragraphs.first.add_text(' with additions')
|
210
|
+
|
211
|
+
# Convert back to HTML
|
212
|
+
modified_html = Prosereflect::Output::Html.convert(document)
|
123
213
|
----
|
124
214
|
|
125
215
|
== Data model
|
@@ -145,12 +235,12 @@ objects.
|
|
145
235
|
| +content |
|
146
236
|
+-------------------+
|
147
237
|
|
|
148
|
-
|
149
|
-
| | |
|
150
|
-
+---v---+ +---v----------+ +-------v--------+
|
151
|
-
|Table | | Paragraph | | Text |
|
152
|
-
| | | | | |
|
153
|
-
+---+---+ +--------------+ +----------------+
|
238
|
+
+----+----+---------------------+-------------+
|
239
|
+
| | | |
|
240
|
+
+---v---+ +---v----------+ +-------v--------+ +-v-----+
|
241
|
+
|Table | | Paragraph | | Text | | User |
|
242
|
+
| | | | | | | |
|
243
|
+
+---+---+ +--------------+ +----------------+ +-------+
|
154
244
|
|
|
155
245
|
|
|
156
246
|
+---v-----------+
|
@@ -193,14 +283,105 @@ Represents a text node.
|
|
193
283
|
|
194
284
|
`text`:: The text content of the node
|
195
285
|
|
286
|
+
=== User
|
287
|
+
|
288
|
+
Represents a user mention in the document.
|
289
|
+
|
290
|
+
`id`:: The unique identifier of the referenced user
|
291
|
+
`type`:: Always set to "user"
|
292
|
+
`content`:: Always empty (user mentions cannot have child nodes)
|
293
|
+
|
196
294
|
=== Table
|
197
295
|
|
198
296
|
Represents a table structure.
|
199
297
|
|
200
|
-
`rows`::
|
201
|
-
`header_row`::
|
202
|
-
`data_rows`:: All rows
|
203
|
-
|
298
|
+
`rows`:: Collection of table rows
|
299
|
+
`header_row`:: First row if it contains header cells
|
300
|
+
`data_rows`:: All non-header rows
|
301
|
+
|
302
|
+
=== Heading
|
303
|
+
|
304
|
+
Represents a heading element (h1-h6).
|
305
|
+
|
306
|
+
`level`:: The heading level (1-6)
|
307
|
+
`text_content`:: Returns the combined text content of all child text nodes
|
308
|
+
`content`:: Collection of child nodes (text, styled text, etc.)
|
309
|
+
|
310
|
+
=== Image
|
311
|
+
|
312
|
+
Represents an image element.
|
313
|
+
|
314
|
+
`src`:: The image source URL
|
315
|
+
`alt`:: Alternative text description
|
316
|
+
`title`:: Image tooltip text
|
317
|
+
`width`:: Image width in pixels
|
318
|
+
`height`:: Image height in pixels
|
319
|
+
|
320
|
+
=== HorizontalRule
|
321
|
+
|
322
|
+
Represents a horizontal rule (hr) element.
|
323
|
+
|
324
|
+
`style`:: Border style (solid, dashed, dotted)
|
325
|
+
`width`:: Rule width (px or %)
|
326
|
+
`thickness`:: Border thickness in pixels
|
327
|
+
|
328
|
+
=== BulletList
|
329
|
+
|
330
|
+
Represents an unordered list.
|
331
|
+
|
332
|
+
`bullet_style`:: List style type (disc, circle, square)
|
333
|
+
`items`:: Collection of list items
|
334
|
+
|
335
|
+
=== OrderedList
|
336
|
+
|
337
|
+
Represents an ordered list.
|
338
|
+
|
339
|
+
`start`:: Starting number for the list
|
340
|
+
`items`:: Collection of list items
|
341
|
+
|
342
|
+
=== ListItem
|
343
|
+
|
344
|
+
Represents a list item within ordered or unordered lists.
|
345
|
+
|
346
|
+
`content`:: Collection of child nodes (can contain paragraphs, nested lists, etc.)
|
347
|
+
`text_content`:: Returns the combined text content
|
348
|
+
|
349
|
+
=== Blockquote
|
350
|
+
|
351
|
+
Represents a blockquote element.
|
352
|
+
|
353
|
+
`citation`:: Optional citation URL
|
354
|
+
`blocks`:: Collection of content blocks within the quote
|
355
|
+
|
356
|
+
=== CodeBlockWrapper
|
357
|
+
|
358
|
+
Container for code blocks with additional attributes.
|
359
|
+
|
360
|
+
`line_numbers`:: Whether to display line numbers
|
361
|
+
`highlight_lines`:: Array of line numbers to highlight
|
362
|
+
`code_blocks`:: Collection of code blocks
|
363
|
+
|
364
|
+
=== CodeBlock
|
365
|
+
|
366
|
+
Represents a code block with syntax highlighting.
|
367
|
+
|
368
|
+
`content`:: The code content
|
369
|
+
`language`:: Programming language for syntax highlighting
|
370
|
+
|
371
|
+
=== Mark
|
372
|
+
|
373
|
+
Base class for text formatting marks.
|
374
|
+
|
375
|
+
==== Available Mark Types
|
376
|
+
|
377
|
+
`Bold`:: Bold text formatting
|
378
|
+
`Italic`:: Italic text formatting
|
379
|
+
`Code`:: Inline code formatting
|
380
|
+
`Link`:: Hyperlink with href attribute
|
381
|
+
`Strike`:: Strikethrough text
|
382
|
+
`Subscript`:: Subscript text
|
383
|
+
`Superscript`:: Superscript text
|
384
|
+
`Underline`:: Underlined text
|
204
385
|
|
205
386
|
=== TableRow
|
206
387
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../attribute'
|
4
|
+
|
5
|
+
module Prosereflect
|
6
|
+
module Attribute
|
7
|
+
class Base < Lutaml::Model::Serializable
|
8
|
+
PM_TYPE = 'attribute'
|
9
|
+
|
10
|
+
attribute :type, :string, default: lambda {
|
11
|
+
begin
|
12
|
+
self.class.const_get(:PM_TYPE)
|
13
|
+
rescue StandardError
|
14
|
+
'attribute'
|
15
|
+
end
|
16
|
+
}
|
17
|
+
attribute :value, :string
|
18
|
+
|
19
|
+
key_value do
|
20
|
+
map 'type', to: :type, render_default: true
|
21
|
+
map 'value', to: :value
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.create(type, value)
|
25
|
+
new(type: type, value: value)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Convert to hash for serialization
|
29
|
+
def to_h
|
30
|
+
{ type => value }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Prosereflect
|
6
|
+
module Attribute
|
7
|
+
class Bold < Base
|
8
|
+
PM_TYPE = 'bold'
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
super
|
12
|
+
self.type = 'bold'
|
13
|
+
end
|
14
|
+
|
15
|
+
def attrs
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Prosereflect
|
6
|
+
module Attribute
|
7
|
+
class Href < Base
|
8
|
+
PM_TYPE = 'href'
|
9
|
+
|
10
|
+
attribute :type, :string, default: -> { PM_TYPE }
|
11
|
+
attribute :href, :string
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
if options.is_a?(String)
|
15
|
+
super()
|
16
|
+
self.value = options
|
17
|
+
else
|
18
|
+
super
|
19
|
+
self.value = options[:href] if options[:href]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Prosereflect
|
6
|
+
module Attribute
|
7
|
+
class Id < Base
|
8
|
+
PM_TYPE = 'id'
|
9
|
+
|
10
|
+
attribute :type, :string, default: -> { send('const_get', 'PM_TYPE') }
|
11
|
+
attribute :id, :string
|
12
|
+
|
13
|
+
key_value do
|
14
|
+
map 'type', to: :type, render_default: true
|
15
|
+
map 'id', to: :id
|
16
|
+
end
|
17
|
+
|
18
|
+
# Convert to hash for serialization
|
19
|
+
def to_h
|
20
|
+
{ 'id' => id }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'lutaml/model'
|
4
|
+
|
5
|
+
module Prosereflect
|
6
|
+
module Attribute
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'attribute/base'
|
11
|
+
require_relative 'attribute/href'
|
12
|
+
require_relative 'attribute/id'
|
13
|
+
require_relative 'attribute/bold'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'node'
|
4
|
+
require_relative 'paragraph'
|
5
|
+
|
6
|
+
module Prosereflect
|
7
|
+
# It can contain other block-level content like paragraphs, lists, etc.
|
8
|
+
class Blockquote < Node
|
9
|
+
PM_TYPE = 'blockquote'
|
10
|
+
|
11
|
+
attribute :type, :string, default: -> { send('const_get', 'PM_TYPE') }
|
12
|
+
attribute :citation, :string
|
13
|
+
attribute :attrs, :hash
|
14
|
+
|
15
|
+
key_value do
|
16
|
+
map 'type', to: :type, render_default: true
|
17
|
+
map 'content', to: :content
|
18
|
+
map 'attrs', to: :attrs
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(attributes = {})
|
22
|
+
attributes[:content] ||= []
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create(attrs = nil)
|
27
|
+
new(attrs: attrs, content: [])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get all content blocks within the blockquote
|
31
|
+
def blocks
|
32
|
+
return [] unless content
|
33
|
+
|
34
|
+
content
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a content block to the blockquote
|
38
|
+
def add_block(content)
|
39
|
+
add_child(content)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Add multiple content blocks at once
|
43
|
+
def add_blocks(blocks_content)
|
44
|
+
blocks_content.each do |block_content|
|
45
|
+
add_block(block_content)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get block at specific position
|
50
|
+
def block_at(index)
|
51
|
+
return nil if index.negative?
|
52
|
+
|
53
|
+
blocks[index]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Update citation/source for the blockquote
|
57
|
+
def citation=(source)
|
58
|
+
self.attrs ||= {}
|
59
|
+
attrs['citation'] = source
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get citation/source of the blockquote
|
63
|
+
def citation
|
64
|
+
attrs&.[]('citation')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if blockquote has a citation
|
68
|
+
def citation?
|
69
|
+
!citation.nil? && !citation.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Remove citation
|
73
|
+
def remove_citation
|
74
|
+
self.attrs ||= {}
|
75
|
+
attrs.delete('citation')
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_paragraph(text)
|
79
|
+
paragraph = Paragraph.new
|
80
|
+
paragraph.add_text(text)
|
81
|
+
add_child(paragraph)
|
82
|
+
paragraph
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'node'
|
4
|
+
require_relative 'list_item'
|
5
|
+
|
6
|
+
module Prosereflect
|
7
|
+
# BulletList class represents an unordered list in ProseMirror.
|
8
|
+
class BulletList < Node
|
9
|
+
PM_TYPE = 'bullet_list'
|
10
|
+
|
11
|
+
attribute :type, :string, default: -> { send('const_get', 'PM_TYPE') }
|
12
|
+
attribute :bullet_style, :string
|
13
|
+
attribute :attrs, :hash
|
14
|
+
|
15
|
+
key_value do
|
16
|
+
map 'type', to: :type, render_default: true
|
17
|
+
map 'attrs', to: :attrs
|
18
|
+
map 'content', to: :content
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(attributes = {})
|
22
|
+
attributes[:content] ||= []
|
23
|
+
attributes[:attrs] ||= { 'bullet_style' => nil }
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.create(attrs = nil)
|
28
|
+
new(attrs: attrs)
|
29
|
+
end
|
30
|
+
|
31
|
+
def bullet_style=(value)
|
32
|
+
@bullet_style = value
|
33
|
+
self.attrs ||= {}
|
34
|
+
attrs['bullet_style'] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def bullet_style
|
38
|
+
@bullet_style || attrs&.[]('bullet_style')
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_item(text)
|
42
|
+
item = ListItem.new
|
43
|
+
item.add_paragraph(text)
|
44
|
+
add_child(item)
|
45
|
+
item
|
46
|
+
end
|
47
|
+
|
48
|
+
def items
|
49
|
+
return [] unless content
|
50
|
+
|
51
|
+
content
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add multiple items at once
|
55
|
+
def add_items(items_content)
|
56
|
+
items_content.each do |item_content|
|
57
|
+
add_item(item_content)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get item at specific position
|
62
|
+
def item_at(index)
|
63
|
+
return nil if index.negative?
|
64
|
+
|
65
|
+
items[index]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get text content with proper formatting
|
69
|
+
def text_content
|
70
|
+
return '' unless content
|
71
|
+
|
72
|
+
content.map { |item| item.respond_to?(:text_content) ? item.text_content : '' }.join("\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Override to_h to exclude empty attrs
|
76
|
+
def to_h
|
77
|
+
hash = super
|
78
|
+
hash['attrs'] ||= {}
|
79
|
+
hash['attrs']['bullet_style'] = bullet_style
|
80
|
+
hash
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|