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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d6ff9f2f6d02d969211f0334a2595d6a9a9ee6e616f9188cd95fea5f1b229bf
4
- data.tar.gz: 1959c3d2dd380fd570662b975dba42bf8bb0d40c3bf7086952e6b97ba47d76b2
3
+ metadata.gz: e314a874501f623577e3e18a2d69aad4c1d1c901cf2aea3b7feb51d1b854f8cc
4
+ data.tar.gz: 42cb9fca3c8206ea2cba20b0d3229f06bc8420075f49da795849032bdad06da4
5
5
  SHA512:
6
- metadata.gz: 70f1c137763bb37f318654b450257e5a2db658bf256c10247c47fe5699a9ad87df2d2848f05f6ea1e96ca8708ba12e8f2cbbf384784583cd59380ad814b4cabe
7
- data.tar.gz: 7b52dd2dd1ed4f3fdae3024ee6e2b0215eea6ba558534faaa29ac5e824f1f9665ba1702621aa9345202a67efd17a1eccbb4bf01b1113f7a3ff837554989206a5
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', '~> 7.0', '>= 7.0.0'
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
@@ -30,6 +30,8 @@ In your layout:
30
30
  <!DOCTYPE html>
31
31
  <html>
32
32
  <head>
33
+ <!-- ⚠️ If you use `rails-importmap` you have to include the importmap somewhere in the head. -->
34
+ <%= javascript_importmap_tags %>
33
35
  <%= yield :head %>
34
36
  </head>
35
37
  <body>
@@ -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
- safe_join([
22
- scripts_import_map_tag,
23
- preload_tags,
24
- styles_tags,
25
- window_scripts_tags,
26
- web_component_tag
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
- def ckeditor5_assets(preset: :default, **kwargs)
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
- Assets::AssetsBundleHtmlSerializer.new(bundle).to_html
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] High-level configuration preset that defines base editor setup,
17
- # including editor type, plugins, and default configuration
18
- # @param config [Hash] Editor-specific configuration that overrides preset defaults
19
- # @param extra_config [Hash] Additional configuration to be merged with the base config
20
- # @param type [Symbol] Editor type (:classic, :inline, :balloon, :decoupled, :multiroot),
21
- # defaults to preset's type if not specified
22
- # @param initial_data [String] Initial content for the editor
23
- # @param watchdog [Boolean] Whether to enable the CKEditor watchdog feature
24
- # @param editable_height [String, Integer] Height of the editable area (Classic editor only)
25
- # @param language [String] Language code for the editor UI
26
- # @param html_attributes [Hash] Additional HTML attributes for the editor element
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?(CKEditor5::Rails::Presets::PresetBuilder)
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