panda-editor 0.4.0 → 0.5.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/CHANGELOG.md +14 -0
- data/app/services/panda/editor/html_to_editor_js_converter.rb +68 -68
- data/config/importmap.rb +9 -9
- data/lib/panda/editor/asset_loader.rb +30 -32
- data/lib/panda/editor/blocks/alert.rb +10 -10
- data/lib/panda/editor/blocks/base.rb +1 -1
- data/lib/panda/editor/blocks/header.rb +2 -2
- data/lib/panda/editor/blocks/image.rb +11 -11
- data/lib/panda/editor/blocks/list.rb +5 -5
- data/lib/panda/editor/blocks/paragraph.rb +10 -10
- data/lib/panda/editor/blocks/quote.rb +6 -6
- data/lib/panda/editor/blocks/table.rb +6 -6
- data/lib/panda/editor/content.rb +8 -8
- data/lib/panda/editor/engine.rb +7 -9
- data/lib/panda/editor/footnote_registry.rb +6 -6
- data/lib/panda/editor/renderer.rb +31 -31
- data/lib/panda/editor/version.rb +1 -1
- data/lib/panda/editor.rb +14 -14
- data/lib/tasks/assets.rake +28 -28
- data/panda-editor.gemspec +23 -23
- metadata +25 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fbf5326beb171bbb5448b7545bfe87fdef49faac3c82f85c1a64a30cc7aa276d
|
|
4
|
+
data.tar.gz: 05c314fcf2d2b2c5cdfe2d4e2684b392bc2d4cd7231c91870a6f6100a1ca7170
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cab18e487c3e4021cebf6ffbd7c98a4c15103ca96a53cbbbd97b3b377a99d83d7c81de246f620359317d166b5716b56d3105be01d7ab1930c96a9f103164ac1b
|
|
7
|
+
data.tar.gz: 1c3ae8993db2fe42ebd8e4c22c664655aa86961757605d897ca5c4c4553b3c0e4427c203d15b23bfae7ce4d83cfc92d6f02d8aa3dc025f3babb3673bc1850de1
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2025-11-04
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Code quality improvements with Rubocop auto-corrections
|
|
12
|
+
- Fixed 1263 style violations across the codebase
|
|
13
|
+
- Converted double-quoted strings to single quotes where appropriate
|
|
14
|
+
- Improved code consistency and readability
|
|
15
|
+
- Corrected string literal usage throughout
|
|
16
|
+
- Fixed extra spacing and formatting issues
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- All tests passing (53 examples, 0 failures)
|
|
20
|
+
- Improved code maintainability
|
|
21
|
+
|
|
8
22
|
## [0.4.0] - 2025-10-30
|
|
9
23
|
|
|
10
24
|
### Added
|
|
@@ -9,78 +9,78 @@ module Panda
|
|
|
9
9
|
return {} if html.blank?
|
|
10
10
|
|
|
11
11
|
# If it's already in EditorJS format, return as is
|
|
12
|
-
return html if html.is_a?(Hash) && (html[
|
|
12
|
+
return html if html.is_a?(Hash) && (html['blocks'].present? || html[:blocks].present?)
|
|
13
13
|
|
|
14
14
|
begin
|
|
15
15
|
# Parse the HTML content
|
|
16
16
|
doc = Nokogiri::HTML.fragment(html.to_s)
|
|
17
|
-
raise ConversionError,
|
|
17
|
+
raise ConversionError, 'Failed to parse HTML content' unless doc
|
|
18
18
|
|
|
19
19
|
blocks = []
|
|
20
|
-
current_text =
|
|
20
|
+
current_text = ''
|
|
21
21
|
|
|
22
22
|
doc.children.each do |node|
|
|
23
23
|
case node.name
|
|
24
|
-
when
|
|
24
|
+
when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
|
25
25
|
# Add any accumulated text as a paragraph before the header
|
|
26
26
|
if current_text.present?
|
|
27
27
|
blocks << create_paragraph_block(current_text)
|
|
28
|
-
current_text =
|
|
28
|
+
current_text = ''
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
blocks << {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
'type' => 'header',
|
|
33
|
+
'data' => {
|
|
34
|
+
'text' => node.text.strip,
|
|
35
|
+
'level' => node.name[1].to_i
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
when
|
|
38
|
+
when 'p', 'div'
|
|
39
39
|
# Add any accumulated text first
|
|
40
40
|
if current_text.present?
|
|
41
41
|
blocks << create_paragraph_block(current_text)
|
|
42
|
-
current_text =
|
|
42
|
+
current_text = ''
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
if node.name ==
|
|
45
|
+
if node.name == 'div'
|
|
46
46
|
# Process div children separately
|
|
47
47
|
node.children.each do |child|
|
|
48
48
|
case child.name
|
|
49
|
-
when
|
|
49
|
+
when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
|
50
50
|
blocks << {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
'type' => 'header',
|
|
52
|
+
'data' => {
|
|
53
|
+
'text' => child.text.strip,
|
|
54
|
+
'level' => child.name[1].to_i
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
when
|
|
57
|
+
when 'p'
|
|
58
58
|
text = process_inline_elements(child)
|
|
59
59
|
paragraphs = text.split(%r{<br\s*/?>\s*<br\s*/?>}).map(&:strip)
|
|
60
60
|
paragraphs.each do |paragraph|
|
|
61
61
|
blocks << create_paragraph_block(paragraph) if paragraph.present?
|
|
62
62
|
end
|
|
63
|
-
when
|
|
64
|
-
items = child.css(
|
|
63
|
+
when 'ul', 'ol'
|
|
64
|
+
items = child.css('li').map { |li| process_inline_elements(li) }
|
|
65
65
|
next if items.empty?
|
|
66
66
|
|
|
67
67
|
blocks << {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
'type' => 'list',
|
|
69
|
+
'data' => {
|
|
70
|
+
'style' => child.name == 'ul' ? 'unordered' : 'ordered',
|
|
71
|
+
'items' => items
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
when
|
|
74
|
+
when 'blockquote'
|
|
75
75
|
blocks << {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
'type' => 'quote',
|
|
77
|
+
'data' => {
|
|
78
|
+
'text' => process_inline_elements(child),
|
|
79
|
+
'caption' => '',
|
|
80
|
+
'alignment' => 'left'
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
when
|
|
83
|
+
when 'text'
|
|
84
84
|
text = child.text.strip
|
|
85
85
|
current_text += text if text.present?
|
|
86
86
|
end
|
|
@@ -93,41 +93,41 @@ module Panda
|
|
|
93
93
|
blocks << create_paragraph_block(paragraph) if paragraph.present?
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
|
-
when
|
|
96
|
+
when 'br'
|
|
97
97
|
current_text += "\n\n"
|
|
98
|
-
when
|
|
98
|
+
when 'text'
|
|
99
99
|
text = node.text.strip
|
|
100
100
|
current_text += text if text.present?
|
|
101
|
-
when
|
|
101
|
+
when 'ul', 'ol'
|
|
102
102
|
# Add any accumulated text first
|
|
103
103
|
if current_text.present?
|
|
104
104
|
blocks << create_paragraph_block(current_text)
|
|
105
|
-
current_text =
|
|
105
|
+
current_text = ''
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
items = node.css(
|
|
108
|
+
items = node.css('li').map { |li| process_inline_elements(li) }
|
|
109
109
|
next if items.empty?
|
|
110
110
|
|
|
111
111
|
blocks << {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
'type' => 'list',
|
|
113
|
+
'data' => {
|
|
114
|
+
'style' => node.name == 'ul' ? 'unordered' : 'ordered',
|
|
115
|
+
'items' => items
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
when
|
|
118
|
+
when 'blockquote'
|
|
119
119
|
# Add any accumulated text first
|
|
120
120
|
if current_text.present?
|
|
121
121
|
blocks << create_paragraph_block(current_text)
|
|
122
|
-
current_text =
|
|
122
|
+
current_text = ''
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
blocks << {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
'type' => 'quote',
|
|
127
|
+
'data' => {
|
|
128
|
+
'text' => process_inline_elements(node),
|
|
129
|
+
'caption' => '',
|
|
130
|
+
'alignment' => 'left'
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
end
|
|
@@ -138,11 +138,11 @@ module Panda
|
|
|
138
138
|
|
|
139
139
|
# Return the complete EditorJS structure
|
|
140
140
|
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
'time' => Time.current.to_i * 1000,
|
|
142
|
+
'blocks' => blocks,
|
|
143
|
+
'version' => '2.28.2'
|
|
144
144
|
}
|
|
145
|
-
rescue => e
|
|
145
|
+
rescue StandardError => e
|
|
146
146
|
Rails.logger.error "HTML to EditorJS conversion failed: #{e.message}"
|
|
147
147
|
Rails.logger.error e.backtrace.join("\n")
|
|
148
148
|
raise ConversionError, "Failed to convert HTML to EditorJS format: #{e.message}"
|
|
@@ -151,41 +151,41 @@ module Panda
|
|
|
151
151
|
|
|
152
152
|
def self.create_paragraph_block(text)
|
|
153
153
|
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
'type' => 'paragraph',
|
|
155
|
+
'data' => {
|
|
156
|
+
'text' => text.strip
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def self.process_inline_elements(node)
|
|
162
|
-
result =
|
|
162
|
+
result = ''
|
|
163
163
|
node.children.each do |child|
|
|
164
164
|
case child.name
|
|
165
|
-
when
|
|
166
|
-
result +=
|
|
167
|
-
when
|
|
165
|
+
when 'br'
|
|
166
|
+
result += '<br>'
|
|
167
|
+
when 'text'
|
|
168
168
|
result += child.text
|
|
169
|
-
when
|
|
169
|
+
when 'strong', 'b'
|
|
170
170
|
result += "<b>#{child.text}</b>"
|
|
171
|
-
when
|
|
171
|
+
when 'em', 'i'
|
|
172
172
|
result += "<i>#{child.text}</i>"
|
|
173
|
-
when
|
|
174
|
-
href = child[
|
|
173
|
+
when 'a'
|
|
174
|
+
href = child['href']
|
|
175
175
|
text = child.text.strip
|
|
176
176
|
# Handle email links specially
|
|
177
|
-
if href&.start_with?(
|
|
178
|
-
email = href.sub(
|
|
177
|
+
if href&.start_with?('mailto:')
|
|
178
|
+
email = href.sub('mailto:', '')
|
|
179
179
|
result += "<a href=\"mailto:#{email}\">#{text}</a>"
|
|
180
180
|
else
|
|
181
181
|
result += "<a href=\"#{href}\">#{text}</a>"
|
|
182
182
|
end
|
|
183
183
|
else
|
|
184
184
|
result += if child.text?
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
child.text
|
|
186
|
+
else
|
|
187
|
+
child.to_html
|
|
188
|
+
end
|
|
189
189
|
end
|
|
190
190
|
end
|
|
191
191
|
result.strip
|
data/config/importmap.rb
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Pin npm packages by running ./bin/importmap
|
|
4
4
|
|
|
5
|
-
pin_all_from
|
|
5
|
+
pin_all_from 'app/javascript/panda/editor', under: 'panda/editor'
|
|
6
6
|
|
|
7
7
|
# EditorJS Core and plugins (from CDN)
|
|
8
|
-
pin
|
|
9
|
-
pin
|
|
10
|
-
pin
|
|
11
|
-
pin
|
|
12
|
-
pin
|
|
13
|
-
pin
|
|
14
|
-
pin
|
|
15
|
-
pin
|
|
8
|
+
pin '@editorjs/editorjs', to: 'https://cdn.jsdelivr.net/npm/@editorjs/editorjs@2.28.2/+esm'
|
|
9
|
+
pin '@editorjs/paragraph', to: 'https://cdn.jsdelivr.net/npm/@editorjs/paragraph@2.11.3/+esm'
|
|
10
|
+
pin '@editorjs/header', to: 'https://cdn.jsdelivr.net/npm/@editorjs/header@2.8.1/+esm'
|
|
11
|
+
pin '@editorjs/nested-list', to: 'https://cdn.jsdelivr.net/npm/@editorjs/nested-list@1.4.2/+esm'
|
|
12
|
+
pin '@editorjs/quote', to: 'https://cdn.jsdelivr.net/npm/@editorjs/quote@2.6.0/+esm'
|
|
13
|
+
pin '@editorjs/simple-image', to: 'https://cdn.jsdelivr.net/npm/@editorjs/simple-image@1.6.0/+esm'
|
|
14
|
+
pin '@editorjs/table', to: 'https://cdn.jsdelivr.net/npm/@editorjs/table@2.3.0/+esm'
|
|
15
|
+
pin '@editorjs/embed', to: 'https://cdn.jsdelivr.net/npm/@editorjs/embed@2.7.0/+esm'
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'json'
|
|
5
5
|
|
|
6
6
|
module Panda
|
|
7
7
|
module Editor
|
|
8
8
|
class AssetLoader
|
|
9
|
-
GITHUB_RELEASES_URL =
|
|
10
|
-
ASSET_CACHE_DIR = Rails.root.join(
|
|
9
|
+
GITHUB_RELEASES_URL = 'https://api.github.com/repos/tastybamboo/panda-editor/releases/latest'
|
|
10
|
+
ASSET_CACHE_DIR = Rails.root.join('tmp', 'panda_editor_assets')
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
13
|
def load_assets
|
|
@@ -22,7 +22,7 @@ module Panda
|
|
|
22
22
|
if use_compiled_assets?
|
|
23
23
|
compiled_javascript_url
|
|
24
24
|
else
|
|
25
|
-
|
|
25
|
+
'/assets/panda/editor/application.js'
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ module Panda
|
|
|
30
30
|
if use_compiled_assets?
|
|
31
31
|
compiled_stylesheet_url
|
|
32
32
|
else
|
|
33
|
-
|
|
33
|
+
'/assets/panda/editor/application.css'
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -39,7 +39,7 @@ module Panda
|
|
|
39
39
|
def use_compiled_assets?
|
|
40
40
|
Rails.env.production? ||
|
|
41
41
|
Rails.env.test? ||
|
|
42
|
-
ENV[
|
|
42
|
+
ENV['PANDA_EDITOR_USE_COMPILED_ASSETS'] == 'true'
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def load_compiled_assets
|
|
@@ -52,23 +52,23 @@ module Panda
|
|
|
52
52
|
|
|
53
53
|
def load_development_assets
|
|
54
54
|
{
|
|
55
|
-
javascript:
|
|
56
|
-
stylesheet:
|
|
55
|
+
javascript: '/assets/panda/editor/application.js',
|
|
56
|
+
stylesheet: '/assets/panda/editor/application.css'
|
|
57
57
|
}
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def compiled_javascript_url
|
|
61
|
-
asset_path = find_latest_asset(
|
|
61
|
+
asset_path = find_latest_asset('js')
|
|
62
62
|
asset_path ? "/panda-editor-assets/#{File.basename(asset_path)}" : nil
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def compiled_stylesheet_url
|
|
66
|
-
asset_path = find_latest_asset(
|
|
66
|
+
asset_path = find_latest_asset('css')
|
|
67
67
|
asset_path ? "/panda-editor-assets/#{File.basename(asset_path)}" : nil
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def find_latest_asset(extension)
|
|
71
|
-
pattern = Rails.root.join(
|
|
71
|
+
pattern = Rails.root.join('public', 'panda-editor-assets', "panda-editor-*.#{extension}")
|
|
72
72
|
Dir.glob(pattern).max_by { |f| File.mtime(f) }
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -79,18 +79,18 @@ module Panda
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def assets_exist?
|
|
82
|
-
js_exists = Dir.glob(Rails.root.join(
|
|
83
|
-
css_exists = Dir.glob(Rails.root.join(
|
|
82
|
+
js_exists = Dir.glob(Rails.root.join('public', 'panda-editor-assets', 'panda-editor-*.js')).any?
|
|
83
|
+
css_exists = Dir.glob(Rails.root.join('public', 'panda-editor-assets', 'panda-editor-*.css')).any?
|
|
84
84
|
js_exists && css_exists
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def download_assets_from_github
|
|
88
|
-
Rails.logger.info
|
|
88
|
+
Rails.logger.info '[Panda Editor] Downloading assets from GitHub releases...'
|
|
89
89
|
|
|
90
90
|
begin
|
|
91
91
|
release_data = fetch_latest_release
|
|
92
|
-
download_release_assets(release_data[
|
|
93
|
-
rescue => e
|
|
92
|
+
download_release_assets(release_data['assets'])
|
|
93
|
+
rescue StandardError => e
|
|
94
94
|
Rails.logger.error "[Panda Editor] Failed to download assets: #{e.message}"
|
|
95
95
|
use_fallback_assets
|
|
96
96
|
end
|
|
@@ -100,32 +100,30 @@ module Panda
|
|
|
100
100
|
uri = URI(GITHUB_RELEASES_URL)
|
|
101
101
|
response = Net::HTTP.get_response(uri)
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
raise "GitHub API returned #{response.code}"
|
|
107
|
-
end
|
|
103
|
+
raise "GitHub API returned #{response.code}" unless response.code == '200'
|
|
104
|
+
|
|
105
|
+
JSON.parse(response.body)
|
|
108
106
|
end
|
|
109
107
|
|
|
110
108
|
def download_release_assets(assets)
|
|
111
109
|
assets.each do |asset|
|
|
112
|
-
next unless asset[
|
|
110
|
+
next unless asset['name'].match?(/panda-editor.*\.(js|css)$/)
|
|
113
111
|
|
|
114
112
|
download_asset(asset)
|
|
115
113
|
end
|
|
116
114
|
end
|
|
117
115
|
|
|
118
116
|
def download_asset(asset)
|
|
119
|
-
uri = URI(asset[
|
|
117
|
+
uri = URI(asset['browser_download_url'])
|
|
120
118
|
response = Net::HTTP.get_response(uri)
|
|
121
119
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
return unless response.code == '200'
|
|
121
|
+
|
|
122
|
+
save_asset(asset['name'], response.body)
|
|
125
123
|
end
|
|
126
124
|
|
|
127
125
|
def save_asset(filename, content)
|
|
128
|
-
dir = Rails.root.join(
|
|
126
|
+
dir = Rails.root.join('public', 'panda-editor-assets')
|
|
129
127
|
FileUtils.mkdir_p(dir)
|
|
130
128
|
|
|
131
129
|
File.write(dir.join(filename), content)
|
|
@@ -133,17 +131,17 @@ module Panda
|
|
|
133
131
|
end
|
|
134
132
|
|
|
135
133
|
def use_fallback_assets
|
|
136
|
-
Rails.logger.warn
|
|
134
|
+
Rails.logger.warn '[Panda Editor] Using fallback embedded assets'
|
|
137
135
|
# Copy embedded assets from gem to public directory
|
|
138
136
|
copy_embedded_assets
|
|
139
137
|
end
|
|
140
138
|
|
|
141
139
|
def copy_embedded_assets
|
|
142
|
-
source_dir = Panda::Editor::Engine.root.join(
|
|
143
|
-
dest_dir = Rails.root.join(
|
|
140
|
+
source_dir = Panda::Editor::Engine.root.join('public', 'panda-editor-assets')
|
|
141
|
+
dest_dir = Rails.root.join('public', 'panda-editor-assets')
|
|
144
142
|
|
|
145
143
|
FileUtils.mkdir_p(dest_dir)
|
|
146
|
-
FileUtils.cp_r(Dir.glob(source_dir.join(
|
|
144
|
+
FileUtils.cp_r(Dir.glob(source_dir.join('*')), dest_dir)
|
|
147
145
|
end
|
|
148
146
|
end
|
|
149
147
|
end
|
|
@@ -5,13 +5,13 @@ module Panda
|
|
|
5
5
|
module Blocks
|
|
6
6
|
class Alert < Base
|
|
7
7
|
def render
|
|
8
|
-
message = sanitize(data[
|
|
9
|
-
type = data[
|
|
8
|
+
message = sanitize(data['message'])
|
|
9
|
+
type = data['type'] || 'primary'
|
|
10
10
|
|
|
11
11
|
html_safe(
|
|
12
12
|
"<div class=\"#{alert_classes(type)} p-4 mb-4 rounded-lg\">" \
|
|
13
13
|
"#{message}" \
|
|
14
|
-
|
|
14
|
+
'</div>'
|
|
15
15
|
)
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -19,13 +19,13 @@ module Panda
|
|
|
19
19
|
|
|
20
20
|
def alert_classes(type)
|
|
21
21
|
case type
|
|
22
|
-
when
|
|
23
|
-
when
|
|
24
|
-
when
|
|
25
|
-
when
|
|
26
|
-
when
|
|
27
|
-
when
|
|
28
|
-
else
|
|
22
|
+
when 'primary' then 'bg-blue-100 text-blue-800'
|
|
23
|
+
when 'secondary' then 'bg-gray-100 text-gray-800'
|
|
24
|
+
when 'success' then 'bg-green-100 text-green-800'
|
|
25
|
+
when 'danger' then 'bg-red-100 text-red-800'
|
|
26
|
+
when 'warning' then 'bg-yellow-100 text-yellow-800'
|
|
27
|
+
when 'info' then 'bg-indigo-100 text-indigo-800'
|
|
28
|
+
else 'bg-blue-100 text-blue-800'
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -5,19 +5,19 @@ module Panda
|
|
|
5
5
|
module Blocks
|
|
6
6
|
class Image < Base
|
|
7
7
|
def render
|
|
8
|
-
url = data[
|
|
9
|
-
caption = sanitize(data[
|
|
10
|
-
with_border = data[
|
|
11
|
-
with_background = data[
|
|
12
|
-
stretched = data[
|
|
8
|
+
url = data['url']
|
|
9
|
+
caption = sanitize(data['caption'])
|
|
10
|
+
with_border = data['withBorder']
|
|
11
|
+
with_background = data['withBackground']
|
|
12
|
+
stretched = data['stretched']
|
|
13
13
|
|
|
14
|
-
css_classes = [
|
|
15
|
-
css_classes <<
|
|
16
|
-
css_classes <<
|
|
17
|
-
css_classes <<
|
|
14
|
+
css_classes = ['prose']
|
|
15
|
+
css_classes << 'border' if with_border
|
|
16
|
+
css_classes << 'bg-gray-100' if with_background
|
|
17
|
+
css_classes << 'w-full' if stretched
|
|
18
18
|
|
|
19
19
|
html_safe(<<~HTML)
|
|
20
|
-
<figure class="#{css_classes.join(
|
|
20
|
+
<figure class="#{css_classes.join(' ')}">
|
|
21
21
|
<img src="#{url}" alt="#{caption}" />
|
|
22
22
|
#{caption_element(caption)}
|
|
23
23
|
</figure>
|
|
@@ -27,7 +27,7 @@ module Panda
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
29
|
def caption_element(caption)
|
|
30
|
-
return
|
|
30
|
+
return '' if caption.blank?
|
|
31
31
|
|
|
32
32
|
"<figcaption>#{caption}</figcaption>"
|
|
33
33
|
end
|
|
@@ -5,10 +5,10 @@ module Panda
|
|
|
5
5
|
module Blocks
|
|
6
6
|
class List < Base
|
|
7
7
|
def render
|
|
8
|
-
list_type =
|
|
8
|
+
list_type = data['style'] == 'ordered' ? 'ol' : 'ul'
|
|
9
9
|
html_safe(
|
|
10
10
|
"<#{list_type}>" \
|
|
11
|
-
"#{render_items(data[
|
|
11
|
+
"#{render_items(data['items'])}" \
|
|
12
12
|
"</#{list_type}>"
|
|
13
13
|
)
|
|
14
14
|
end
|
|
@@ -17,14 +17,14 @@ module Panda
|
|
|
17
17
|
|
|
18
18
|
def render_items(items)
|
|
19
19
|
items.map do |item|
|
|
20
|
-
content = item.is_a?(Hash) ? item[
|
|
21
|
-
nested =
|
|
20
|
+
content = item.is_a?(Hash) ? item['content'] : item
|
|
21
|
+
nested = item.is_a?(Hash) && item['items'].present? ? render_nested(item['items']) : ''
|
|
22
22
|
"<li>#{sanitize(content)}#{nested}</li>"
|
|
23
23
|
end.join
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def render_nested(items)
|
|
27
|
-
self.class.new({
|
|
27
|
+
self.class.new({ 'items' => items, 'style' => data['style'] }).render
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|