ckeditor5 1.19.4 → 1.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -2
- data/README.md +2 -0
- data/lib/ckeditor5/rails/assets/assets_bundle_html_serializer.rb +43 -25
- data/lib/ckeditor5/rails/cdn/helpers.rb +81 -2
- data/lib/ckeditor5/rails/context/helpers.rb +28 -0
- data/lib/ckeditor5/rails/context/preset_builder.rb +33 -0
- data/lib/ckeditor5/rails/editor/helpers/config_helpers.rb +39 -0
- data/lib/ckeditor5/rails/editor/helpers/editor_helpers.rb +73 -13
- data/lib/ckeditor5/rails/engine.rb +11 -3
- data/lib/ckeditor5/rails/hooks/form.rb +44 -0
- data/lib/ckeditor5/rails/hooks/importmap.rb +58 -0
- data/lib/ckeditor5/rails/hooks/simple_form.rb +42 -0
- data/lib/ckeditor5/rails/presets/concerns/configuration_methods.rb +45 -0
- data/lib/ckeditor5/rails/presets/concerns/plugin_methods.rb +54 -0
- data/lib/ckeditor5/rails/presets/manager.rb +49 -0
- data/lib/ckeditor5/rails/presets/plugins_builder.rb +32 -0
- data/lib/ckeditor5/rails/presets/preset_builder.rb +117 -0
- data/lib/ckeditor5/rails/presets/toolbar_builder.rb +33 -0
- data/lib/ckeditor5/rails/version.rb +1 -1
- data/spec/e2e/features/editor_types_spec.rb +1 -1
- data/spec/e2e/spec_helper.rb +2 -7
- data/spec/lib/ckeditor5/rails/assets/{asset_bundle_hml_serializer_spec.rb → assets_bundle_hml_serializer_spec.rb} +58 -15
- data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +41 -5
- data/spec/lib/ckeditor5/rails/engine_spec.rb +32 -0
- data/spec/lib/ckeditor5/rails/hooks/importmap_spec.rb +131 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e314a874501f623577e3e18a2d69aad4c1d1c901cf2aea3b7feb51d1b854f8cc
|
4
|
+
data.tar.gz: 42cb9fca3c8206ea2cba20b0d3229f06bc8420075f49da795849032bdad06da4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f99b51b674897abae5b231e5e34f71a4e64963a2a88b16ef9f5f00dba1a9f17998f5bbfd7a4e1a19c6abd6716601f4c0bcbb978003a04938d2fa1a5b4ebbdf28
|
7
|
+
data.tar.gz: 050a3bf881dca0d54e186fe01066c8b7a4112d0f818e84f80de4ba8301cdd53500dc88cef800259ea9b2c1b0a23a42f93d3cce4fe0618206540a9bc9281222a8
|
data/Gemfile
CHANGED
@@ -8,17 +8,20 @@ group :development do
|
|
8
8
|
gem 'brakeman', '~> 6.1', '>= 6.1.1', require: false
|
9
9
|
gem 'guard', '~> 2.19', '>= 2.19.0'
|
10
10
|
gem 'guard-process', '~> 1.2'
|
11
|
+
gem 'importmap-rails'
|
12
|
+
gem 'propshaft'
|
11
13
|
gem 'pry', '~> 0.15', '>= 0.15.0'
|
12
14
|
gem 'pry-rails', '~> 0.3', '>= 0.3.11'
|
13
|
-
gem 'rails', '~>
|
15
|
+
gem 'rails', '~> 8.0', '>= 8.0.0'
|
14
16
|
gem 'rake', '~> 13.2', '>= 13.2.1'
|
15
17
|
gem 'rubocop', '~> 1.66', require: false
|
16
18
|
gem 'rubocop-rails', '~> 2.26', '>= 2.26.2', require: false
|
17
19
|
gem 'rubocop-rails-omakase', '~> 1.0.0', require: false
|
18
20
|
gem 'simple_form', '~> 5.3', '>= 5.3.0'
|
19
21
|
gem 'slim', '~> 5.2', '>= 5.2.0'
|
20
|
-
gem 'sprockets-rails', '~> 3.2', '>= 3.2.2'
|
21
22
|
gem 'sqlite3', '>= 1.4'
|
23
|
+
gem 'stimulus-rails'
|
24
|
+
gem 'turbo-rails'
|
22
25
|
end
|
23
26
|
|
24
27
|
group :test, :development do
|
data/README.md
CHANGED
@@ -9,22 +9,26 @@ module CKEditor5::Rails::Assets
|
|
9
9
|
class AssetsBundleHtmlSerializer
|
10
10
|
include ActionView::Helpers::TagHelper
|
11
11
|
|
12
|
-
attr_reader :bundle
|
12
|
+
attr_reader :bundle, :importmap
|
13
13
|
|
14
|
-
def initialize(bundle)
|
14
|
+
def initialize(bundle, importmap: true)
|
15
15
|
raise TypeError, 'bundle must be an instance of AssetsBundle' unless bundle.is_a?(AssetsBundle)
|
16
16
|
|
17
|
+
@importmap = importmap
|
17
18
|
@bundle = bundle
|
18
19
|
end
|
19
20
|
|
20
21
|
def to_html
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
tags = [
|
23
|
+
preload_tags,
|
24
|
+
styles_tags,
|
25
|
+
window_scripts_tags,
|
26
|
+
web_component_tag
|
27
|
+
]
|
28
|
+
|
29
|
+
tags.prepend(AssetsImportMap.new(bundle).to_html) if importmap
|
30
|
+
|
31
|
+
safe_join(tags)
|
28
32
|
end
|
29
33
|
|
30
34
|
def self.url_resource_preload_type(url)
|
@@ -47,22 +51,6 @@ module CKEditor5::Rails::Assets
|
|
47
51
|
end)
|
48
52
|
end
|
49
53
|
|
50
|
-
def scripts_import_map_tag
|
51
|
-
return @scripts_import_map_tag if defined?(@scripts_import_map_tag)
|
52
|
-
|
53
|
-
import_map = bundle.scripts.each_with_object({}) do |script, map|
|
54
|
-
next if !script.esm? || looks_like_url?(script.import_name)
|
55
|
-
|
56
|
-
map[script.import_name] = script.url
|
57
|
-
end
|
58
|
-
|
59
|
-
@scripts_import_map_tag = tag.script(
|
60
|
-
{ imports: import_map }.to_json.html_safe,
|
61
|
-
type: 'importmap',
|
62
|
-
nonce: true
|
63
|
-
)
|
64
|
-
end
|
65
|
-
|
66
54
|
def styles_tags
|
67
55
|
@styles_tags ||= safe_join(bundle.stylesheets.map do |url|
|
68
56
|
tag.link(href: url, rel: 'stylesheet', crossorigin: 'anonymous')
|
@@ -86,6 +74,36 @@ module CKEditor5::Rails::Assets
|
|
86
74
|
end
|
87
75
|
end)
|
88
76
|
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class AssetsImportMap
|
80
|
+
include ActionView::Helpers::TagHelper
|
81
|
+
|
82
|
+
attr_reader :bundle
|
83
|
+
|
84
|
+
def initialize(bundle)
|
85
|
+
@bundle = bundle
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_json(*_args)
|
89
|
+
import_map = bundle.scripts.each_with_object({}) do |script, map|
|
90
|
+
next if !script.esm? || looks_like_url?(script.import_name)
|
91
|
+
|
92
|
+
map[script.import_name] = script.url
|
93
|
+
end
|
94
|
+
|
95
|
+
{ imports: import_map }.to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_html
|
99
|
+
tag.script(
|
100
|
+
to_json.html_safe,
|
101
|
+
type: 'importmap',
|
102
|
+
nonce: true
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
89
107
|
|
90
108
|
def looks_like_url?(str)
|
91
109
|
uri = URI.parse(str)
|
@@ -12,7 +12,58 @@ require_relative 'ckbox_bundle'
|
|
12
12
|
|
13
13
|
module CKEditor5::Rails
|
14
14
|
module Cdn::Helpers
|
15
|
-
|
15
|
+
class ImportmapAlreadyRenderedError < ArgumentError; end
|
16
|
+
|
17
|
+
# The `ckeditor5_assets` helper includes CKEditor 5 assets in your application.
|
18
|
+
# It's responsible for generating the necessary JavaScript and CSS imports based on
|
19
|
+
# the specified preset and configuration.
|
20
|
+
#
|
21
|
+
# @param [Symbol, PresetBuilder] preset The name of the preset to use (default: :default)
|
22
|
+
# or a PresetBuilder object created using ckeditor5_preset helper
|
23
|
+
# @param [Hash] kwargs Additional configuration options:
|
24
|
+
# - version: Specify a custom CKEditor version
|
25
|
+
# - cdn: Select CDN provider (:jsdelivr, :unpkg, etc.)
|
26
|
+
# - translations: Array of language codes to include
|
27
|
+
# - ckbox: Configuration hash for CKBox integration
|
28
|
+
# - license_key: Commercial license key
|
29
|
+
# - premium: Enable premium features
|
30
|
+
# - language: Set editor UI language (e.g. :pl, :es)
|
31
|
+
#
|
32
|
+
# @example Basic usage with default preset
|
33
|
+
# <%= ckeditor5_assets %>
|
34
|
+
# <%= ckeditor5_editor %>
|
35
|
+
#
|
36
|
+
# @example Simple editor with custom configuration
|
37
|
+
# <%= ckeditor5_assets preset: :basic %>
|
38
|
+
# <%= ckeditor5_editor toolbar: [:bold, :italic], plugins: [:Bold, :Italic] %>
|
39
|
+
#
|
40
|
+
# @example Using custom preset with translations and language
|
41
|
+
# <%= ckeditor5_assets preset: :custom, translations: [:pl, :es], language: :pl %>
|
42
|
+
#
|
43
|
+
# @example Commercial usage with license key
|
44
|
+
# <%= ckeditor5_assets license_key: 'your-license-key' %>
|
45
|
+
#
|
46
|
+
# @example Using preset builder object
|
47
|
+
# <% @preset = ckeditor5_preset do
|
48
|
+
# version '43.3.1'
|
49
|
+
# toolbar :bold, :italic
|
50
|
+
# plugins :Bold, :Italic
|
51
|
+
# end %>
|
52
|
+
# <%= ckeditor5_assets preset: @preset %>
|
53
|
+
#
|
54
|
+
# @example Editor only configuration with different types
|
55
|
+
# <%= ckeditor5_assets preset: :basic %>
|
56
|
+
# <%= ckeditor5_editor type: :classic %>
|
57
|
+
# <%= ckeditor5_editor type: :inline %>
|
58
|
+
# <%= ckeditor5_editor type: :balloon %>
|
59
|
+
#
|
60
|
+
def ckeditor5_assets(
|
61
|
+
preset: :default,
|
62
|
+
importmap: true,
|
63
|
+
**kwargs
|
64
|
+
)
|
65
|
+
ensure_importmap_not_rendered!
|
66
|
+
|
16
67
|
mapped_preset = merge_with_editor_preset(preset, **kwargs)
|
17
68
|
mapped_preset => {
|
18
69
|
cdn:,
|
@@ -34,7 +85,7 @@ module CKEditor5::Rails
|
|
34
85
|
preset: mapped_preset
|
35
86
|
}
|
36
87
|
|
37
|
-
|
88
|
+
build_html_tags(bundle, importmap)
|
38
89
|
end
|
39
90
|
|
40
91
|
Cdn::UrlGenerator::CDN_THIRD_PARTY_GENERATORS.each_key do |key|
|
@@ -105,5 +156,33 @@ module CKEditor5::Rails
|
|
105
156
|
bundle << plugin.preload_assets_bundle if plugin.preload_assets_bundle.present?
|
106
157
|
end
|
107
158
|
end
|
159
|
+
|
160
|
+
def importmap_available?
|
161
|
+
respond_to?(:importmap_rendered?)
|
162
|
+
end
|
163
|
+
|
164
|
+
def ensure_importmap_not_rendered!
|
165
|
+
return unless importmap_available? && importmap_rendered?
|
166
|
+
|
167
|
+
raise ImportmapAlreadyRenderedError,
|
168
|
+
'CKEditor5 assets must be included before javascript_importmap_tags. ' \
|
169
|
+
'Please move ckeditor5_assets helper before javascript_importmap_tags in your layout.'
|
170
|
+
end
|
171
|
+
|
172
|
+
def build_html_tags(bundle, importmap)
|
173
|
+
serializer = Assets::AssetsBundleHtmlSerializer.new(
|
174
|
+
bundle,
|
175
|
+
importmap: importmap && !importmap_available?
|
176
|
+
)
|
177
|
+
|
178
|
+
html = serializer.to_html
|
179
|
+
|
180
|
+
if importmap_available?
|
181
|
+
@__ckeditor_context[:html_tags] = html
|
182
|
+
nil
|
183
|
+
else
|
184
|
+
html
|
185
|
+
end
|
186
|
+
end
|
108
187
|
end
|
109
188
|
end
|
@@ -5,6 +5,22 @@ require_relative 'preset_serializer'
|
|
5
5
|
|
6
6
|
module CKEditor5::Rails::Context
|
7
7
|
module Helpers
|
8
|
+
# Creates a CKEditor context component that can be shared between multiple editors.
|
9
|
+
# This allows you to define common plugins that will be available to all editors
|
10
|
+
# within the context.
|
11
|
+
#
|
12
|
+
# @param [PresetBuilder] preset The preset object containing shared plugins configuration
|
13
|
+
# @yield The block where editor instances should be defined
|
14
|
+
#
|
15
|
+
# @example Basic usage with shared plugins
|
16
|
+
# <% preset = ckeditor5_context_preset do
|
17
|
+
# plugins :Mention, :Emoji # These plugins will be shared across all editors
|
18
|
+
# end %>
|
19
|
+
#
|
20
|
+
# <%= ckeditor5_context(preset) do %>
|
21
|
+
# <%= ckeditor5_editor preset: :content %>
|
22
|
+
# <%= ckeditor5_editor preset: :description %>
|
23
|
+
# <% end %>
|
8
24
|
def ckeditor5_context(preset = nil, &block)
|
9
25
|
preset ||= PresetBuilder.new
|
10
26
|
context_props = PresetSerializer.new(preset)
|
@@ -12,6 +28,18 @@ module CKEditor5::Rails::Context
|
|
12
28
|
tag.public_send(:'ckeditor-context-component', **context_props.to_attributes, &block)
|
13
29
|
end
|
14
30
|
|
31
|
+
# Creates a new preset builder object for use with ckeditor5_context.
|
32
|
+
# Used to define shared plugins that will be available to all editors within the context.
|
33
|
+
# Note: Only plugins configuration is relevant for context, other settings like toolbar
|
34
|
+
# should be configured at the editor level.
|
35
|
+
#
|
36
|
+
# @yield Block for configuring the shared plugins
|
37
|
+
# @return [PresetBuilder] A new preset builder instance
|
38
|
+
#
|
39
|
+
# @example Creating a context with shared plugins
|
40
|
+
# <% preset = ckeditor5_context_preset do
|
41
|
+
# plugins :Comments, :TrackChanges, :Collaboration # Shared functionality plugins
|
42
|
+
# end %>
|
15
43
|
def ckeditor5_context_preset(&block)
|
16
44
|
PresetBuilder.new(&block)
|
17
45
|
end
|
@@ -5,10 +5,39 @@ require_relative '../presets/concerns/plugin_methods'
|
|
5
5
|
|
6
6
|
module CKEditor5::Rails
|
7
7
|
module Context
|
8
|
+
# PresetBuilder provides functionality for building CKEditor 5 presets.
|
9
|
+
#
|
10
|
+
# This class includes configuration and plugin handling methods from concerns
|
11
|
+
# and allows defining presets either through initialization blocks or method chaining.
|
12
|
+
#
|
13
|
+
# @example Basic preset definition
|
14
|
+
# preset = PresetBuilder.new do
|
15
|
+
# version '43.3.1'
|
16
|
+
# gpl
|
17
|
+
# type :classic
|
18
|
+
# toolbar :bold, :italic
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example Cloning and modifying a preset
|
22
|
+
# new_preset = preset.clone
|
23
|
+
# new_preset.toolbar do
|
24
|
+
# append :underline
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @see Presets::Concerns::ConfigurationMethods
|
28
|
+
# @see Presets::Concerns::PluginMethods
|
8
29
|
class PresetBuilder
|
9
30
|
include Presets::Concerns::ConfigurationMethods
|
10
31
|
include Presets::Concerns::PluginMethods
|
11
32
|
|
33
|
+
# Initializes a new preset builder with optional configuration block
|
34
|
+
#
|
35
|
+
# @param block [Proc] Optional configuration block
|
36
|
+
# @example Initialize with block
|
37
|
+
# PresetBuilder.new do
|
38
|
+
# version '43.3.1'
|
39
|
+
# toolbar :bold, :italic
|
40
|
+
# end
|
12
41
|
def initialize(&block)
|
13
42
|
@config = {
|
14
43
|
plugins: []
|
@@ -17,6 +46,10 @@ module CKEditor5::Rails
|
|
17
46
|
instance_eval(&block) if block_given?
|
18
47
|
end
|
19
48
|
|
49
|
+
# Creates a deep copy of the preset builder
|
50
|
+
#
|
51
|
+
# @param source [PresetBuilder] Source preset to copy from
|
52
|
+
# @return [PresetBuilder] New preset instance with copied configuration
|
20
53
|
def initialize_copy(source)
|
21
54
|
super
|
22
55
|
|
@@ -2,10 +2,49 @@
|
|
2
2
|
|
3
3
|
module CKEditor5::Rails::Editor::Helpers
|
4
4
|
module Config
|
5
|
+
# Creates a reference to a DOM element that will be used by CKEditor's features.
|
6
|
+
# This is particularly useful for features that need to check element presence
|
7
|
+
# or operate on specific DOM elements.
|
8
|
+
#
|
9
|
+
# @param selector [String] CSS selector for the target element
|
10
|
+
# @return [Hash] A hash with the element reference in CKEditor's format
|
11
|
+
#
|
12
|
+
# @example Referencing an element in plugin configuration
|
13
|
+
# configure :yourPlugin, {
|
14
|
+
# element: ckeditor5_element_ref("body")
|
15
|
+
# }
|
5
16
|
def ckeditor5_element_ref(selector)
|
6
17
|
{ '$element': selector }
|
7
18
|
end
|
8
19
|
|
20
|
+
# Creates or retrieves a preset configuration for CKEditor.
|
21
|
+
# When called with a name, finds and returns an existing preset.
|
22
|
+
# When called with a block, creates a new preset with the given configuration.
|
23
|
+
#
|
24
|
+
# @param name [Symbol, nil] The name of an existing preset to retrieve
|
25
|
+
# @yield Block for configuring a new preset
|
26
|
+
# @return [PresetBuilder] The preset configuration object
|
27
|
+
#
|
28
|
+
# @example Finding an existing preset
|
29
|
+
# @preset = ckeditor5_preset(:default)
|
30
|
+
#
|
31
|
+
# @example Creating a custom preset in controller
|
32
|
+
# @preset = ckeditor5_preset do
|
33
|
+
# version '43.3.1'
|
34
|
+
# toolbar :sourceEditing, :|, :bold, :italic
|
35
|
+
# plugins :Essentials, :Paragraph, :Bold, :Italic
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @example Using preset in view
|
39
|
+
# <%= ckeditor5_assets preset: @preset %>
|
40
|
+
# <%= ckeditor5_editor %>
|
41
|
+
#
|
42
|
+
# @example Overriding existing preset
|
43
|
+
# @preset = ckeditor5_preset(:default).override do
|
44
|
+
# toolbar do
|
45
|
+
# remove :underline, :heading
|
46
|
+
# end
|
47
|
+
# end
|
9
48
|
def ckeditor5_preset(name = nil, &block)
|
10
49
|
return CKEditor5::Rails::Engine.find_preset(name) if name
|
11
50
|
|
@@ -11,19 +11,53 @@ module CKEditor5::Rails
|
|
11
11
|
class EditorContextError < StandardError; end
|
12
12
|
class PresetNotFoundError < ArgumentError; end
|
13
13
|
|
14
|
-
# Creates a CKEditor 5 editor instance
|
15
|
-
#
|
16
|
-
# @param preset [Symbol]
|
17
|
-
#
|
18
|
-
# @param
|
19
|
-
# @param
|
20
|
-
# @param
|
21
|
-
#
|
22
|
-
# @param
|
23
|
-
# @param
|
24
|
-
# @param
|
25
|
-
#
|
26
|
-
# @
|
14
|
+
# Creates a CKEditor 5 editor instance in the view.
|
15
|
+
#
|
16
|
+
# @param preset [Symbol, PresetBuilder] The name of the preset or a PresetBuilder object
|
17
|
+
# @param config [Hash] Custom editor configuration that overrides preset configuration
|
18
|
+
# @param extra_config [Hash] Additional configuration to merge with preset/custom config
|
19
|
+
# @param type [Symbol] Editor type (:classic, :inline, :balloon, :decoupled, :multiroot)
|
20
|
+
# @param initial_data [String] Initial HTML content for the editor
|
21
|
+
# @param watchdog [Boolean] Enable/disable the editor crash recovery (default: true)
|
22
|
+
# @param editable_height [Integer] Set fixed height for editor in pixels
|
23
|
+
# @param language [Symbol] Set editor UI language (e.g. :pl, :es)
|
24
|
+
# @param html_attributes [Hash] Additional HTML attributes for editor element
|
25
|
+
#
|
26
|
+
# @example Basic usage with default preset
|
27
|
+
# <%= ckeditor5_editor %>
|
28
|
+
#
|
29
|
+
# @example Custom preset with specific height and initial content
|
30
|
+
# <%= ckeditor5_editor preset: :custom, editable_height: 300, initial_data: "<p>Hello</p>" %>
|
31
|
+
#
|
32
|
+
# @example Inline editor with custom styling
|
33
|
+
# <%= ckeditor5_editor type: :inline, style: 'width: 600px' %>
|
34
|
+
#
|
35
|
+
# @example Multiroot editor with multiple editable areas
|
36
|
+
# <%= ckeditor5_editor type: :multiroot do %>
|
37
|
+
# <%= ckeditor5_toolbar %>
|
38
|
+
# <%= ckeditor5_editable 'title', style: 'border: 1px solid gray' %>
|
39
|
+
# <%= ckeditor5_editable 'content' %>
|
40
|
+
# <% end %>
|
41
|
+
#
|
42
|
+
# @example Decoupled editor with custom UI layout
|
43
|
+
# <%= ckeditor5_editor type: :decoupled do %>
|
44
|
+
# <div class="toolbar-container">
|
45
|
+
# <%= ckeditor5_toolbar %>
|
46
|
+
# </div>
|
47
|
+
# <div class="editor-container">
|
48
|
+
# <%= ckeditor5_editable %>
|
49
|
+
# </div>
|
50
|
+
# <% end %>
|
51
|
+
#
|
52
|
+
# @example Editor with event handlers
|
53
|
+
# <%= ckeditor5_editor oneditorchange: 'handleChange',
|
54
|
+
# oneditorready: 'handleReady',
|
55
|
+
# oneditorerror: 'handleError' %>
|
56
|
+
#
|
57
|
+
# @example Form integration
|
58
|
+
# <%= form_for @post do |f| %>
|
59
|
+
# <%= f.ckeditor5 :content, required: true %>
|
60
|
+
# <% end %>
|
27
61
|
def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
|
28
62
|
preset: nil,
|
29
63
|
config: nil, extra_config: {}, type: nil,
|
@@ -51,18 +85,44 @@ module CKEditor5::Rails
|
|
51
85
|
tag.public_send(:'ckeditor-component', **tag_attributes, &block)
|
52
86
|
end
|
53
87
|
|
88
|
+
# Creates an editable area for multiroot or decoupled editors.
|
89
|
+
#
|
90
|
+
# @param name [String] Identifier for the editable area
|
91
|
+
# @param kwargs [Hash] HTML attributes for the editable element
|
92
|
+
#
|
93
|
+
# @example Creating a named editable area in multiroot editor
|
94
|
+
# <%= ckeditor5_editable 'content', style: 'border: 1px solid gray' %>
|
54
95
|
def ckeditor5_editable(name = nil, **kwargs, &block)
|
55
96
|
tag.public_send(:'ckeditor-editable-component', name: name, **kwargs, &block)
|
56
97
|
end
|
57
98
|
|
99
|
+
# Creates a UI part component for the editor (toolbar, menubar).
|
100
|
+
#
|
101
|
+
# @param name [String] Name of the UI component ('toolbar', 'menuBarView')
|
102
|
+
# @param kwargs [Hash] HTML attributes for the UI component
|
103
|
+
#
|
104
|
+
# @example Creating a toolbar component
|
105
|
+
# <%= ckeditor5_ui_part 'toolbar' %>
|
58
106
|
def ckeditor5_ui_part(name, **kwargs, &block)
|
59
107
|
tag.public_send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
|
60
108
|
end
|
61
109
|
|
110
|
+
# Creates a toolbar component for decoupled editor.
|
111
|
+
#
|
112
|
+
# @param kwargs [Hash] HTML attributes for the toolbar element
|
113
|
+
#
|
114
|
+
# @example Creating a toolbar with custom class
|
115
|
+
# <%= ckeditor5_toolbar class: 'custom-toolbar' %>
|
62
116
|
def ckeditor5_toolbar(**kwargs)
|
63
117
|
ckeditor5_ui_part('toolbar', **kwargs)
|
64
118
|
end
|
65
119
|
|
120
|
+
# Creates a menubar component for decoupled editor.
|
121
|
+
#
|
122
|
+
# @param kwargs [Hash] HTML attributes for the menubar element
|
123
|
+
#
|
124
|
+
# @example Creating a menubar with custom styling
|
125
|
+
# <%= ckeditor5_menubar style: 'margin-bottom: 10px' %>
|
66
126
|
def ckeditor5_menubar(**kwargs)
|
67
127
|
ckeditor5_ui_part('menuBarView', **kwargs)
|
68
128
|
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
require 'rails/engine'
|
4
4
|
|
5
5
|
require_relative 'presets/manager'
|
6
|
-
require_relative 'hooks/form'
|
7
|
-
|
8
6
|
require_relative 'plugins/simple_upload_adapter'
|
9
7
|
require_relative 'plugins/wproofreader'
|
10
8
|
|
@@ -30,6 +28,16 @@ module CKEditor5::Rails
|
|
30
28
|
ActionView::Helpers::FormBuilder.include(Hooks::Form::FormBuilderExtension)
|
31
29
|
end
|
32
30
|
|
31
|
+
initializer 'ckeditor5.importmap', after: :load_config_initializers, before: 'importmap' do |_app|
|
32
|
+
require_relative 'hooks/importmap'
|
33
|
+
|
34
|
+
ActiveSupport.on_load(:action_view) do
|
35
|
+
if defined?(::Importmap::ImportmapTagsHelper)
|
36
|
+
::Importmap::ImportmapTagsHelper.prepend(Hooks::Importmap::ImportmapTagsHelper)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
class << self
|
34
42
|
def base
|
35
43
|
config.ckeditor5
|
@@ -45,7 +53,7 @@ module CKEditor5::Rails
|
|
45
53
|
end
|
46
54
|
|
47
55
|
def find_preset(preset)
|
48
|
-
return preset if preset.is_a?(
|
56
|
+
return preset if preset.is_a?(Presets::PresetBuilder)
|
49
57
|
|
50
58
|
base.presets[preset]
|
51
59
|
end
|
@@ -48,6 +48,50 @@ module CKEditor5::Rails::Hooks
|
|
48
48
|
end
|
49
49
|
|
50
50
|
module FormBuilderExtension
|
51
|
+
# Creates a CKEditor 5 field for the specified form attribute.
|
52
|
+
#
|
53
|
+
# @param method [Symbol] The model attribute to edit
|
54
|
+
# @param options [Hash] Options for customizing the editor
|
55
|
+
#
|
56
|
+
# @option options [Symbol] :preset (:default) The preset configuration to use
|
57
|
+
# @option options [Symbol] :type (:classic) Editor type (classic, inline, balloon, decoupled)
|
58
|
+
# @option options [Hash] :config Custom editor configuration
|
59
|
+
# @option options [String] :initial_data Initial content for the editor
|
60
|
+
# @option options [Boolean] :required (false) Whether the field is required
|
61
|
+
# @option options [String] :class CSS classes for the editor
|
62
|
+
# @option options [String] :style Inline CSS styles
|
63
|
+
#
|
64
|
+
# @example Basic usage
|
65
|
+
# <%= form_for @post do |f| %>
|
66
|
+
# <%= f.ckeditor5 :content %>
|
67
|
+
# <% end %>
|
68
|
+
#
|
69
|
+
# @example With custom styling and required field
|
70
|
+
# <%= form_for @post do |f| %>
|
71
|
+
# <%= f.ckeditor5 :content,
|
72
|
+
# style: 'width: 700px',
|
73
|
+
# required: true,
|
74
|
+
# initial_data: 'Hello World!'
|
75
|
+
# %>
|
76
|
+
# <% end %>
|
77
|
+
#
|
78
|
+
# @example Using custom preset and type
|
79
|
+
# <%= form_for @post do |f| %>
|
80
|
+
# <%= f.ckeditor5 :content,
|
81
|
+
# preset: :custom,
|
82
|
+
# type: :inline,
|
83
|
+
# class: 'custom-editor'
|
84
|
+
# %>
|
85
|
+
# <% end %>
|
86
|
+
#
|
87
|
+
# @example Simple Form integration
|
88
|
+
# <%= simple_form_for @post do |f| %>
|
89
|
+
# <%= f.input :content,
|
90
|
+
# as: :ckeditor5,
|
91
|
+
# input_html: { style: 'width: 600px' },
|
92
|
+
# required: true
|
93
|
+
# %>
|
94
|
+
# <% end %>
|
51
95
|
def ckeditor5(method, options = {})
|
52
96
|
EditorInputBuilder.new(object_name, object, @template)
|
53
97
|
.build_editor(method, options)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CKEditor5::Rails::Hooks
|
4
|
+
module Importmap
|
5
|
+
module ImportmapTagsHelper
|
6
|
+
def javascript_importmap_tags(entry_point = 'application', importmap: Rails.application.importmap)
|
7
|
+
@importmap_rendered = true
|
8
|
+
serialized_tags = [
|
9
|
+
javascript_importmap_module_preload_tags(importmap, entry_point: entry_point),
|
10
|
+
javascript_import_module_tag(entry_point)
|
11
|
+
]
|
12
|
+
|
13
|
+
importmap_json = prepare_importmap_json(importmap)
|
14
|
+
|
15
|
+
if @__ckeditor_context.present?
|
16
|
+
process_ckeditor_context(serialized_tags, importmap_json)
|
17
|
+
else
|
18
|
+
serialized_tags.prepend(javascript_inline_importmap_tag(importmap_json))
|
19
|
+
end
|
20
|
+
|
21
|
+
safe_join(serialized_tags, "\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def importmap_rendered?
|
25
|
+
@importmap_rendered
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def prepare_importmap_json(importmap)
|
31
|
+
importmap.to_json(resolver: self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_ckeditor_context(serialized_tags, importmap_json)
|
35
|
+
bundle = @__ckeditor_context.fetch(:bundle)
|
36
|
+
merged_json = merge_ckeditor_importmap(bundle, importmap_json)
|
37
|
+
|
38
|
+
serialized_tags.prepend(javascript_inline_importmap_tag(merged_json))
|
39
|
+
serialized_tags.append(@__ckeditor_context[:html_tags])
|
40
|
+
end
|
41
|
+
|
42
|
+
def merge_ckeditor_importmap(bundle, base_importmap_json)
|
43
|
+
ckeditor_json = CKEditor5::Rails::Assets::AssetsImportMap.new(bundle).to_json
|
44
|
+
merge_import_maps_json(ckeditor_json, base_importmap_json)
|
45
|
+
end
|
46
|
+
|
47
|
+
def merge_import_maps_json(a_json, b_json)
|
48
|
+
a = JSON.parse(a_json)
|
49
|
+
b = JSON.parse(b_json)
|
50
|
+
a['imports'].merge!(b['imports'])
|
51
|
+
a.to_json
|
52
|
+
rescue JSON::ParserError => e
|
53
|
+
Rails.logger.error "Failed to merge import maps: #{e.message}"
|
54
|
+
b_json
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|