ckeditor5 1.19.5 → 1.20.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/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
|