ckeditor5 1.11.1 โ†’ 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ff16eff85125b3b08d827a0dc6f25f85c84535ffdc37fada565a256e9a494c6
4
- data.tar.gz: 505f43e893d3fa0119fd23365c83a01b729edbbc9236adcaa282c5e6a0791ac4
3
+ metadata.gz: 6b4d6c95733dd5744553bb787f20f9efaf474086c6268d6842561905747ec10f
4
+ data.tar.gz: 17052fae7a4e0e88fcc7c0af8ab397c8fadb3421ce611c64a10fe597d15a9d82
5
5
  SHA512:
6
- metadata.gz: 7d93cdc48b6c6f8d10af216f4c0622dbd99ac08c82267ab6c3d5089f1a70395cda15b9ec345b097b40ae59b8611c492711fefc8b6a33d09a42110bf294077465
7
- data.tar.gz: 60ea317d04dbc4305cf878925234b28eeb380e508f1991f9021be042e1d88d35ae11914947e1aeca088846b2cad91d2625b79cfb09dafe661fcd32b1e4080acf
6
+ metadata.gz: 4dbaefb77863ceaeaad0805d430c86b90c7fbec675a7478fc55994a9706bfc3b8ec59c7791ce916389346da508f47a88ce2ff8c38a79d1c9189aa77405769f9f
7
+ data.tar.gz: 6b1d599bbfe28b35c81d037b052085c15fe55c1248f8e858efc77b7515c88b5ea2818187082f0db7810192b7d2cdfd3bb45d3dba57f7cf03cd0f53932dfbb202
data/Gemfile CHANGED
@@ -8,7 +8,9 @@ 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 'guard-rspec', '~> 4.7', '>= 4.7.3'
11
12
  gem 'pry', '~> 0.15', '>= 0.15.0'
13
+ gem 'pry-rails', '~> 0.3', '>= 0.3.11'
12
14
  gem 'rails', '~> 7.0', '>= 7.0.0'
13
15
  gem 'rake', '~> 13.2', '>= 13.2.1'
14
16
  gem 'rspec', '~> 3.13'
@@ -17,6 +19,8 @@ group :development do
17
19
  gem 'rubocop', '~> 1.66', require: false
18
20
  gem 'rubocop-rails', '~> 2.26', '>= 2.26.2', require: false
19
21
  gem 'rubocop-rails-omakase', '~> 1.0.0', require: false
22
+ gem 'simplecov', '~> 0.21', '>= 0.21.2', require: false
23
+ gem 'simplecov_json_formatter', '~> 0.1.4', require: false
20
24
  gem 'simple_form', '~> 5.3', '>= 5.3.0'
21
25
  gem 'slim', '~> 5.2', '>= 5.2.0'
22
26
  gem 'sprockets-rails', '~> 3.2', '>= 3.2.2'
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![License: GPL v2](https://img.shields.io/badge/License-GPL_v2-blue.svg?style=flat-square)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
4
4
  ![Gem Version](https://img.shields.io/gem/v/ckeditor5?style=flat-square)
5
5
  ![Gem Total Downloads](https://img.shields.io/gem/dt/ckeditor5?style=flat-square&color=orange)
6
+ [![Coverage](https://img.shields.io/codecov/c/github/mati365/ckeditor5-rails?style=flat-square)](https://codecov.io/gh/mati365/ckeditor5-rails)
6
7
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-green.svg?style=flat-square)](http://makeapullrequest.com)
7
8
  ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/mati365/ckeditor5-rails?style=flat-square)
8
9
  [![GitHub issues](https://img.shields.io/github/issues/mati365/ckeditor5-rails?style=flat-square)](https://github.com/Mati365/ckeditor5-rails/issues)
@@ -84,7 +85,10 @@ Voilร ! You have CKEditor 5 integrated with your Rails application. ๐ŸŽ‰
84
85
  - [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
85
86
  - [`plugins(*names, **kwargs)` method](#pluginsnames-kwargs-method)
86
87
  - [`inline_plugin(name, code)` method](#inline_pluginname-code-method)
88
+ - [`simple_upload_adapter(url)` method](#simple_upload_adapterurl-method)
89
+ - [Controller / View helpers ๐Ÿ“ฆ](#controller--view-helpers-)
87
90
  - [`ckeditor5_element_ref(selector)` method](#ckeditor5_element_refselector-method)
91
+ - [`ckeditor5_preset(&block)` method](#ckeditor5_presetblock-method)
88
92
  - [Including CKEditor 5 assets ๐Ÿ“ฆ](#including-ckeditor-5-assets-)
89
93
  - [Format ๐Ÿ“](#format-)
90
94
  - [Using default preset](#using-default-preset)
@@ -118,6 +122,7 @@ Voilร ! You have CKEditor 5 integrated with your Rails application. ๐ŸŽ‰
118
122
  - [`editor-change` event](#editor-change-event)
119
123
  - [Inline event handling](#inline-event-handling)
120
124
  - [Gem Development ๐Ÿ› ](#gem-development-)
125
+ - [Running tests ๐Ÿงช](#running-tests-)
121
126
  - [Trademarks ๐Ÿ“œ](#trademarks-)
122
127
  - [License ๐Ÿ“œ](#license-)
123
128
 
@@ -327,7 +332,7 @@ Defines the CKBox plugin to be included in the editor. The example below shows h
327
332
  CKEditor5::Rails.configure do
328
333
  # ... other configuration
329
334
 
330
- ckbox '2.5.4', theme: :lark
335
+ ckbox '2.6.0', theme: :lark
331
336
  end
332
337
  ```
333
338
 
@@ -530,6 +535,20 @@ CKEditor5::Rails.configure do
530
535
  end
531
536
  ```
532
537
 
538
+ Methods such as `remove`, `append`, and `prepend` can be used to modify the plugins configuration. To remove a plugin, you can use the `remove` method with the plugin name as an argument:
539
+
540
+ ```rb
541
+ # config/initializers/ckeditor5.rb
542
+
543
+ CKEditor5::Rails.configure do
544
+ # ... other configuration
545
+
546
+ plugins do
547
+ remove :Heading
548
+ end
549
+ end
550
+ ```
551
+
533
552
  #### `inline_plugin(name, code)` method
534
553
 
535
554
  Use with caution as this is an inline definition of the plugin code, and you can define a custom class or function for the plugin here. The example below shows how to define a custom plugin that highlights the text:
@@ -556,6 +575,37 @@ CKEditor5::Rails.configure do
556
575
  end
557
576
  ```
558
577
 
578
+ #### `simple_upload_adapter(url)` method
579
+
580
+ Defines the URL for the simple upload adapter. The default endpoint is `/uploads` and the method is `POST`. The example below shows how to set the URL to `/uploads`:
581
+
582
+ ```rb
583
+ # config/initializers/ckeditor5.rb
584
+
585
+ CKEditor5::Rails.configure do
586
+ # ... other configuration
587
+
588
+ simple_upload_adapter
589
+ # or: simple_upload_adapter '/uploads'
590
+ end
591
+ ```
592
+
593
+ Remember to remove the `Base64UploadAdapter` plugin from the plugins list if you want to use the simple upload adapter. It may cause issues.
594
+
595
+ ```rb
596
+ # config/initializers/ckeditor5.rb
597
+
598
+ CKEditor5::Rails.configure do
599
+ # ... other configuration
600
+
601
+ plugins do
602
+ remove :Base64UploadAdapter
603
+ end
604
+ end
605
+ ```
606
+
607
+ ### Controller / View helpers ๐Ÿ“ฆ
608
+
559
609
  #### `ckeditor5_element_ref(selector)` method
560
610
 
561
611
  Defines a reference to a CKEditor 5 element. In other words, it allows you to reference DOM elements that will be used by the editor's features. It's particularly useful for features that need to check element presence or operate on specific DOM elements. The primary example is the `presence list` feature that requires a reference to the element that will be used to display the list.
@@ -572,6 +622,43 @@ CKEditor5::Rails.configure do
572
622
  end
573
623
  ```
574
624
 
625
+ #### `ckeditor5_preset(&block)` method
626
+
627
+ The `ckeditor5_preset` method allows you to define a custom preset in your application controller. It may be useful when you want to define a preset based on the current user or request.
628
+
629
+ ```rb
630
+ # app/controllers/application_controller.rb
631
+
632
+ class ApplicationController < ActionController::Base
633
+ def show
634
+ @preset = ckeditor5_preset do
635
+ version '43.3.0'
636
+
637
+ toolbar :sourceEditing, :|, :bold, :italic, :underline, :strikethrough,
638
+ :subscript, :superscript, :removeFormat, :|, :bulletedList, :numberedList,
639
+ :fontFamily, :fontSize, :|, :link, :anchor, :|,
640
+ :fontColor, :fontBackgroundColor
641
+
642
+ plugins :Essentials, :Paragraph, :Bold, :Italic, :Underline, :Strikethrough,
643
+ :Subscript, :Superscript, :RemoveFormat, :List, :Link, :Font,
644
+ :FontFamily, :FontSize, :FontColor, :FontBackgroundColor, :SourceEditing, :Essentials, :Paragraph
645
+ end
646
+ end
647
+ end
648
+ ```
649
+
650
+ In order to use the preset in the view, you can pass it to the `ckeditor5_assets` helper method:
651
+
652
+ ```erb
653
+ <!-- app/views/demos/index.html.erb -->
654
+
655
+ <% content_for :head do %>
656
+ <%= ckeditor5_assets preset: @preset %>
657
+ <% end %>
658
+
659
+ <%= ckeditor5_editor %>
660
+ ```
661
+
575
662
  ## Including CKEditor 5 assets ๐Ÿ“ฆ
576
663
 
577
664
  To include CKEditor 5 assets in your application, you can use the `ckeditor5_assets` helper method. This method takes the version of CKEditor 5 as an argument and includes the necessary resources of the editor. Depending on the specified configuration, it includes the JS and CSS assets from the official CKEditor 5 CDN or one of the popular CDNs.
@@ -1329,7 +1416,21 @@ If you want to contribute to the gem, you can clone the repository and run the f
1329
1416
  ```sh
1330
1417
  gem install bundler -v 2.5.22
1331
1418
  bundle install
1332
- bundle exec guard
1419
+ bundle exec guard -g rails
1420
+ ```
1421
+
1422
+ ### Running tests ๐Ÿงช
1423
+
1424
+ You can run the tests using the following command:
1425
+
1426
+ ```sh
1427
+ bundle exec rspec
1428
+ ```
1429
+
1430
+ If you want to watch the tests, you can use the following command:
1431
+
1432
+ ```sh
1433
+ bundle exec guard -g rspec
1333
1434
  ```
1334
1435
 
1335
1436
  ## Trademarks ๐Ÿ“œ
@@ -39,7 +39,7 @@ module CKEditor5::Rails
39
39
  private
40
40
 
41
41
  def merge_with_editor_preset(preset, **kwargs)
42
- found_preset = Engine.base.presets[preset]
42
+ found_preset = Engine.find_preset(preset)
43
43
 
44
44
  if found_preset.blank?
45
45
  raise ArgumentError,
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails
4
+ module Editor::Helpers::Config
5
+ def ckeditor5_element_ref(selector)
6
+ { '$element': selector }
7
+ end
8
+
9
+ def ckeditor5_preset(name = nil, &block)
10
+ return Engine.find_preset(name) if name
11
+
12
+ raise ArgumentError, 'Configuration block is required for preset definition' unless block_given?
13
+
14
+ CKEditor5::Rails::Presets::PresetBuilder.new(&block)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config_helpers'
4
+
5
+ module CKEditor5::Rails
6
+ module Editor::Helpers::Editor
7
+ include Editor::Helpers::Config
8
+
9
+ class EditorContextError < StandardError; end
10
+ class PresetNotFoundError < ArgumentError; end
11
+ class InvalidEditableHeightError < ArgumentError; end
12
+
13
+ # Creates a CKEditor 5 editor instance
14
+ #
15
+ # @param preset [Symbol] High-level configuration preset that defines base editor setup,
16
+ # including editor type, plugins, and default configuration
17
+ # @param config [Hash] Editor-specific configuration that overrides preset defaults
18
+ # @param extra_config [Hash] Additional configuration to be merged with the base config
19
+ # @param type [Symbol] Editor type (:classic, :inline, :balloon, :decoupled, :multiroot),
20
+ # defaults to preset's type if not specified
21
+ # @param initial_data [String] Initial content for the editor
22
+ # @param watchdog [Boolean] Whether to enable the CKEditor watchdog feature
23
+ # @param editable_height [String, Integer] Height of the editable area (Classic editor only)
24
+ # @param html_attributes [Hash] Additional HTML attributes for the editor element
25
+ def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
26
+ preset: nil,
27
+ config: nil, extra_config: {}, type: nil,
28
+ initial_data: nil, watchdog: true,
29
+ editable_height: nil,
30
+ **html_attributes, &block
31
+ )
32
+ validate_editor_input!(initial_data, block)
33
+
34
+ controller_context = validate_and_get_editor_context!
35
+
36
+ preset = find_preset(preset || controller_context[:preset] || :default)
37
+ config = build_editor_config(preset, config, extra_config, initial_data)
38
+ type ||= preset.type
39
+
40
+ editor_props = Editor::Props.new(
41
+ controller_context, type, config,
42
+ watchdog: watchdog,
43
+ editable_height: editable_height
44
+ )
45
+
46
+ tag_attributes = html_attributes.merge(editor_props.to_attributes)
47
+
48
+ tag.send(:'ckeditor-component', **tag_attributes, &block)
49
+ end
50
+
51
+ def ckeditor5_editable(name = nil, **kwargs, &block)
52
+ tag.send(:'ckeditor-editable-component', name: name, **kwargs, &block)
53
+ end
54
+
55
+ def ckeditor5_ui_part(name, **kwargs, &block)
56
+ tag.send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
57
+ end
58
+
59
+ def ckeditor5_toolbar(**kwargs)
60
+ ckeditor5_ui_part('toolbar', **kwargs)
61
+ end
62
+
63
+ def ckeditor5_menubar(**kwargs)
64
+ ckeditor5_ui_part('menuBarView', **kwargs)
65
+ end
66
+
67
+ private
68
+
69
+ def validate_editor_input!(initial_data, block)
70
+ return unless initial_data && block
71
+
72
+ raise ArgumentError, 'Cannot pass initial data and block at the same time.'
73
+ end
74
+
75
+ def build_editor_config(preset, config, extra_config, initial_data)
76
+ editor_config = config || preset.config
77
+ editor_config = editor_config.deep_merge(extra_config)
78
+ editor_config[:initialData] = initial_data if initial_data
79
+ editor_config
80
+ end
81
+
82
+ def validate_and_get_editor_context!
83
+ unless defined?(@__ckeditor_context)
84
+ raise EditorContextError,
85
+ 'CKEditor installation context is not defined. ' \
86
+ 'Ensure ckeditor5_assets is called in the head section.'
87
+ end
88
+
89
+ @__ckeditor_context
90
+ end
91
+
92
+ def find_preset(preset)
93
+ Engine.find_preset(preset) or
94
+ raise PresetNotFoundError, "Preset #{preset} is not defined."
95
+ end
96
+ end
97
+ end
@@ -3,107 +3,9 @@
3
3
  require_relative 'props_plugin'
4
4
  require_relative 'props_inline_plugin'
5
5
  require_relative 'props'
6
- require_relative 'config_helpers'
7
6
 
8
- module CKEditor5::Rails
9
- module Editor::Helpers
10
- include Editor::ConfigHelpers
11
-
12
- class EditorContextError < StandardError; end
13
- class PresetNotFoundError < ArgumentError; end
14
- class InvalidEditableHeightError < ArgumentError; end
15
-
16
- def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
17
- config: nil, extra_config: {},
18
- type: nil, preset: nil,
19
- initial_data: nil, watchdog: true,
20
- editable_height: nil,
21
- **html_attributes, &block
22
- )
23
- validate_editor_input!(initial_data, block)
24
-
25
- controller_context = validate_and_get_editor_context!
26
-
27
- preset = resolve_editor_preset(preset || controller_context[:preset])
28
- config = build_editor_config(preset, config, extra_config, initial_data)
29
- type ||= preset.type
30
-
31
- validated_height = validate_editable_height(type, editable_height) || preset.editable_height
32
- editor_props = Editor::Props.new(controller_context, type, config, watchdog: watchdog)
33
-
34
- tag_attributes = html_attributes
35
- .merge(editor_props.to_attributes)
36
- .merge(validated_height ? { 'editable-height' => validated_height } : {})
37
-
38
- tag.send(:'ckeditor-component', **tag_attributes, &block)
39
- end
40
-
41
- def ckeditor5_editable(name = nil, **kwargs, &block)
42
- tag.send(:'ckeditor-editable-component', name: name, **kwargs, &block)
43
- end
44
-
45
- def ckeditor5_ui_part(name, **kwargs, &block)
46
- tag.send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
47
- end
48
-
49
- def ckeditor5_toolbar(**kwargs)
50
- ckeditor5_ui_part('toolbar', **kwargs)
51
- end
52
-
53
- def ckeditor5_menubar(**kwargs)
54
- ckeditor5_ui_part('menuBarView', **kwargs)
55
- end
56
-
57
- private
58
-
59
- def validate_editor_input!(initial_data, block)
60
- return unless initial_data && block
61
-
62
- raise ArgumentError, 'Cannot pass initial data and block at the same time.'
63
- end
64
-
65
- def resolve_editor_preset(preset_name)
66
- fetch_editor_preset(preset_name || :default)
67
- end
68
-
69
- def build_editor_config(preset, config, extra_config, initial_data)
70
- editor_config = config || preset.config
71
- editor_config = editor_config.deep_merge(extra_config)
72
- editor_config[:initialData] = initial_data if initial_data
73
- editor_config
74
- end
75
-
76
- def validate_and_get_editor_context!
77
- unless defined?(@__ckeditor_context)
78
- raise EditorContextError,
79
- 'CKEditor installation context is not defined. ' \
80
- 'Ensure ckeditor5_assets is called in the head section.'
81
- end
82
-
83
- @__ckeditor_context
84
- end
85
-
86
- def fetch_editor_preset(preset)
87
- Engine.base.presets[preset] or
88
- raise PresetNotFoundError, "Preset #{preset} is not defined."
89
- end
90
-
91
- def validate_editable_height(type, height)
92
- return nil if height.nil?
93
-
94
- unless type == :classic
95
- raise InvalidEditableHeightError,
96
- 'editable_height can be used only with ClassicEditor'
97
- end
98
-
99
- case height
100
- when String, /^\d+px$/ then height
101
- when Integer, /^\d+$/ then "#{height}px"
102
- else
103
- raise InvalidEditableHeightError,
104
- "editable_height must be an integer representing pixels or string ending with 'px'\n" \
105
- "(e.g. 500 or '500px'). Got: #{height.inspect}"
106
- end
107
- end
108
- end
7
+ module CKEditor5::Rails::Editor::Helpers
109
8
  end
9
+
10
+ require_relative 'helpers/config_helpers'
11
+ require_relative 'helpers/editor_helpers'
@@ -12,13 +12,14 @@ module CKEditor5::Rails::Editor
12
12
  multiroot: 'MultiRootEditor'
13
13
  }.freeze
14
14
 
15
- def initialize(controller_context, type, config, watchdog: true)
15
+ def initialize(controller_context, type, config, watchdog: true, editable_height: nil)
16
16
  raise ArgumentError, "Invalid editor type: #{type}" unless Props.valid_editor_type?(type)
17
17
 
18
18
  @controller_context = controller_context
19
19
  @watchdog = watchdog
20
20
  @type = type
21
21
  @config = config
22
+ @editable_height = normalize_editable_height(editable_height)
22
23
  end
23
24
 
24
25
  def to_attributes
@@ -34,7 +35,7 @@ module CKEditor5::Rails::Editor
34
35
 
35
36
  private
36
37
 
37
- attr_reader :controller_context, :watchdog, :type, :config
38
+ attr_reader :controller_context, :watchdog, :type, :config, :editable_height
38
39
 
39
40
  def serialized_attributes
40
41
  {
@@ -43,6 +44,7 @@ module CKEditor5::Rails::Editor
43
44
  config: serialize_config,
44
45
  watchdog: watchdog
45
46
  }
47
+ .merge(editable_height ? { 'editable-height' => editable_height } : {})
46
48
  end
47
49
 
48
50
  def serialize_translations
@@ -59,5 +61,23 @@ module CKEditor5::Rails::Editor
59
61
  .tap { |cfg| cfg[:licenseKey] = controller_context[:license_key] if controller_context[:license_key] }
60
62
  .to_json
61
63
  end
64
+
65
+ def normalize_editable_height(editable_height)
66
+ return nil if editable_height.nil?
67
+
68
+ unless type == :classic
69
+ raise InvalidEditableHeightError,
70
+ 'editable_height can be used only with ClassicEditor'
71
+ end
72
+
73
+ case editable_height
74
+ when String, /^\d+px$/ then editable_height
75
+ when Integer, /^\d+$/ then "#{editable_height}px"
76
+ else
77
+ raise InvalidEditableHeightError,
78
+ "editable_height must be an integer representing pixels or string ending with 'px'\n" \
79
+ "(e.g. 500 or '500px'). Got: #{editable_height.inspect}"
80
+ end
81
+ end
62
82
  end
63
83
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module CKEditor5::Rails::Editor
4
4
  class PropsInlinePlugin
5
+ attr_reader :name, :code
6
+
5
7
  def initialize(name, code)
6
8
  @name = name
7
9
  @code = code
@@ -18,8 +20,6 @@ module CKEditor5::Rails::Editor
18
20
 
19
21
  private
20
22
 
21
- attr_reader :name, :code
22
-
23
23
  def validate_code!
24
24
  raise ArgumentError, 'Code must be a String' unless code.is_a?(String)
25
25
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  module CKEditor5::Rails::Editor
4
4
  class PropsPlugin
5
+ attr_reader :name, :js_import_meta
6
+
5
7
  delegate :to_h, to: :import_meta
6
8
 
7
9
  def initialize(name, premium: false, **js_import_meta)
@@ -30,9 +32,5 @@ module CKEditor5::Rails::Editor
30
32
  meta.merge!({ type: :external })
31
33
  meta
32
34
  end
33
-
34
- private
35
-
36
- attr_reader :name, :js_import_meta
37
35
  end
38
36
  end
@@ -4,6 +4,7 @@ require 'rails/engine'
4
4
 
5
5
  require_relative 'presets/manager'
6
6
  require_relative 'hooks/form'
7
+ require_relative 'plugins/simple_upload_adapter'
7
8
 
8
9
  module CKEditor5::Rails
9
10
  class Engine < ::Rails::Engine
@@ -12,6 +13,7 @@ module CKEditor5::Rails
12
13
 
13
14
  initializer 'helper' do
14
15
  ActiveSupport.on_load(:action_view) { include Helpers }
16
+ ActiveSupport.on_load(:action_controller) { include Helpers }
15
17
  end
16
18
 
17
19
  initializer 'ckeditor5.simple_form', after: :load_config_initializers do
@@ -39,6 +41,12 @@ module CKEditor5::Rails
39
41
  proxy = ConfigurationProxy.new(config.ckeditor5)
40
42
  proxy.instance_eval(&block)
41
43
  end
44
+
45
+ def find_preset(preset)
46
+ return preset if preset.is_a?(CKEditor5::Rails::Presets::PresetBuilder)
47
+
48
+ Engine.base.presets[preset]
49
+ end
42
50
  end
43
51
 
44
52
  class ConfigurationProxy
@@ -7,7 +7,8 @@ require_relative 'context/helpers'
7
7
  module CKEditor5::Rails
8
8
  module Helpers
9
9
  include Cdn::Helpers
10
- include Editor::Helpers
10
+ include Editor::Helpers::Config
11
+ include Editor::Helpers::Editor
11
12
  include Context::Helpers
12
13
  end
13
14
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails::Plugins
4
+ class SimpleUploadAdapter < CKEditor5::Rails::Editor::PropsInlinePlugin
5
+ PLUGIN_CODE = <<~JAVASCRIPT
6
+ import { Plugin, FileRepository } from 'ckeditor5';
7
+
8
+ export default class SimpleUploadAdapter extends Plugin {
9
+ static get requires() {
10
+ return [FileRepository];
11
+ }
12
+
13
+ static get pluginName() {
14
+ return 'SimpleUploadAdapter';
15
+ }
16
+
17
+ init() {
18
+ const fileRepository = this.editor.plugins.get(FileRepository);
19
+ const config = this.editor.config.get('simpleUpload');
20
+
21
+ if (!config || !config.uploadUrl) {
22
+ console.warn('Upload URL is not configured');
23
+ return;
24
+ }
25
+
26
+ fileRepository.createUploadAdapter = (loader) => ({
27
+ async upload() {
28
+ try {
29
+ const file = await loader.file;
30
+ const formData = new FormData();
31
+ formData.append('upload', file);
32
+
33
+ return new Promise((resolve, reject) => {
34
+ const xhr = new XMLHttpRequest();
35
+
36
+ xhr.open('POST', config.uploadUrl, true);
37
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
38
+
39
+ xhr.upload.onprogress = (evt) => {
40
+ if (evt.lengthComputable) {
41
+ loader.uploadTotal = evt.total;
42
+ loader.uploaded = evt.loaded;
43
+ }
44
+ };
45
+
46
+ xhr.onload = () => {
47
+ if (xhr.status >= 200 && xhr.status < 300) {
48
+ const data = JSON.parse(xhr.response);
49
+ resolve({ default: data.url });
50
+ } else {
51
+ reject(`Upload failed: ${xhr.statusText}`);
52
+ }
53
+ };
54
+
55
+ xhr.onerror = () => reject('Upload failed');
56
+ xhr.onabort = () => reject('Upload aborted');
57
+
58
+ xhr.send(formData);
59
+ this._xhr = xhr;
60
+ });
61
+ } catch (error) {
62
+ throw error;
63
+ }
64
+ },
65
+
66
+ abort() {
67
+ if (this._xhr) {
68
+ this._xhr.abort();
69
+ }
70
+ }
71
+ });
72
+ }
73
+ }
74
+ JAVASCRIPT
75
+
76
+ def initialize
77
+ super(:SimpleUpload, PLUGIN_CODE)
78
+ end
79
+ end
80
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'preset_builder'
4
3
  require_relative 'toolbar_builder'
4
+ require_relative 'plugins_builder'
5
+ require_relative 'preset_builder'
5
6
 
6
7
  module CKEditor5::Rails::Presets
7
8
  class Manager
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails
4
+ class Presets::PluginsBuilder
5
+ attr_reader :plugins
6
+
7
+ def initialize(plugins)
8
+ @plugins = plugins
9
+ end
10
+
11
+ def self.create_plugin(name, **kwargs)
12
+ if name.is_a?(Editor::PropsInlinePlugin) || name.is_a?(Editor::PropsPlugin)
13
+ name
14
+ else
15
+ Editor::PropsPlugin.new(name, **kwargs)
16
+ end
17
+ end
18
+
19
+ def remove(*names)
20
+ names.each { |name| plugins.delete_if { |plugin| plugin.name == name } }
21
+ end
22
+
23
+ def prepend(*names, before: nil, **kwargs)
24
+ new_plugins = names.map { |name| self.class.create_plugin(name, **kwargs) }
25
+
26
+ if before
27
+ index = plugins.index { |p| p.name == before }
28
+ raise ArgumentError, "Plugin '#{before}' not found" unless index
29
+
30
+ plugins.insert(index, *new_plugins)
31
+ else
32
+ plugins.insert(0, *new_plugins)
33
+ end
34
+ end
35
+
36
+ def append(*names, after: nil, **kwargs)
37
+ new_plugins = names.map { |name| self.class.create_plugin(name, **kwargs) }
38
+
39
+ if after
40
+ index = plugins.index { |p| p.name == after }
41
+ raise ArgumentError, "Plugin '#{after}' not found" unless index
42
+
43
+ plugins.insert(index + 1, *new_plugins)
44
+ else
45
+ plugins.push(*new_plugins)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,15 +3,15 @@
3
3
  module CKEditor5::Rails
4
4
  module Presets
5
5
  class PresetBuilder
6
- include Editor::ConfigHelpers
6
+ include Editor::Helpers::Config
7
7
 
8
8
  attr_reader :config
9
9
 
10
- def initialize
10
+ def initialize(&block)
11
11
  @version = nil
12
12
  @premium = false
13
13
  @cdn = :jsdelivr
14
- @translations = []
14
+ @translations = [:en]
15
15
  @license_key = nil
16
16
  @type = :classic
17
17
  @ckbox = nil
@@ -20,6 +20,8 @@ module CKEditor5::Rails
20
20
  plugins: [],
21
21
  toolbar: []
22
22
  }
23
+
24
+ instance_eval(&block) if block_given?
23
25
  end
24
26
 
25
27
  def premium?
@@ -135,7 +137,7 @@ module CKEditor5::Rails
135
137
 
136
138
  return unless block
137
139
 
138
- builder = ToolbarBuilder.new(@config[:toolbar])
140
+ builder = ArrayBuilder.new(@config[:toolbar][:items])
139
141
  builder.instance_eval(&block)
140
142
  end
141
143
 
@@ -144,11 +146,21 @@ module CKEditor5::Rails
144
146
  end
145
147
 
146
148
  def plugin(name, **kwargs)
147
- @config[:plugins] << Editor::PropsPlugin.new(name, **kwargs)
149
+ plugin_obj = PluginsBuilder.create_plugin(name, **kwargs)
150
+
151
+ @config[:plugins] << plugin_obj
152
+ plugin_obj
148
153
  end
149
154
 
150
- def plugins(*names, **kwargs)
151
- names.each { |name| plugin(name, **kwargs) }
155
+ def plugins(*names, **kwargs, &block)
156
+ @config[:plugins] ||= []
157
+
158
+ names.each { |name| plugin(name, **kwargs) } unless names.empty?
159
+
160
+ return unless block
161
+
162
+ builder = PluginsBuilder.new(@config[:plugins])
163
+ builder.instance_eval(&block)
152
164
  end
153
165
 
154
166
  def language(ui = nil, content: ui) # rubocop:disable Naming/MethodParameterName
@@ -159,6 +171,15 @@ module CKEditor5::Rails
159
171
  content: content
160
172
  }
161
173
  end
174
+
175
+ def simple_upload_adapter(upload_url = '/uploads')
176
+ plugins do
177
+ remove :Base64UploadAdapter
178
+ end
179
+
180
+ plugin(Plugins::SimpleUploadAdapter.new)
181
+ configure(:simpleUpload, { uploadUrl: upload_url })
182
+ end
162
183
  end
163
184
  end
164
185
  end
@@ -2,12 +2,10 @@
2
2
 
3
3
  module CKEditor5::Rails::Presets
4
4
  class ToolbarBuilder
5
- def initialize(toolbar_config)
6
- @toolbar_config = toolbar_config
7
- end
5
+ attr_reader :items
8
6
 
9
- def items
10
- @toolbar_config[:items]
7
+ def initialize(items)
8
+ @items = items
11
9
  end
12
10
 
13
11
  def remove(*removed_items)
@@ -17,7 +15,7 @@ module CKEditor5::Rails::Presets
17
15
  def prepend(*prepended_items, before: nil)
18
16
  if before
19
17
  index = items.index(before)
20
- raise ArgumentError, "Item '#{before}' not found in toolbar" unless index
18
+ raise ArgumentError, "Item '#{before}' not found in array" unless index
21
19
 
22
20
  items.insert(index, *prepended_items)
23
21
  else
@@ -28,7 +26,7 @@ module CKEditor5::Rails::Presets
28
26
  def append(*appended_items, after: nil)
29
27
  if after
30
28
  index = items.index(after)
31
- raise ArgumentError, "Item '#{after}' not found in toolbar" unless index
29
+ raise ArgumentError, "Item '#{after}' not found in array" unless index
32
30
 
33
31
  items.insert(index + 1, *appended_items)
34
32
  else
@@ -13,7 +13,6 @@ class CKEditor5::Rails::Semver
13
13
  private
14
14
 
15
15
  def validate!
16
- raise ArgumentError, 'version must be a string' unless version.is_a?(String)
17
16
  raise ArgumentError, 'invalid version format' unless version.match?(/\A\d+\.\d+\.\d+\z/)
18
17
  end
19
18
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.11.1'
5
+ VERSION = '1.14.0'
6
6
  end
7
7
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Cdn::UrlGenerator do
6
+ let(:test_class) do
7
+ Class.new do
8
+ include CKEditor5::Rails::Cdn::UrlGenerator
9
+
10
+ attr_writer :cdn
11
+ end
12
+ end
13
+
14
+ let(:instance) { test_class.new }
15
+
16
+ shared_examples 'a CDN provider' do |cdn, url_pattern|
17
+ before { instance.cdn = cdn }
18
+
19
+ it 'generates correct URL' do
20
+ expect(instance.create_cdn_url('ckeditor5', '34.1.0', 'ckeditor.js'))
21
+ .to eq(format(url_pattern, version: '34.1.0'))
22
+ end
23
+ end
24
+
25
+ describe '#create_cdn_url' do
26
+ context 'with open-source CDNs' do
27
+ it_behaves_like 'a CDN provider', :jsdelivr,
28
+ 'https://cdn.jsdelivr.net/npm/ckeditor5@%<version>s/dist/browser/ckeditor.js'
29
+
30
+ it_behaves_like 'a CDN provider', :unpkg,
31
+ 'https://unpkg.com/ckeditor5@%<version>s/dist/browser/ckeditor.js'
32
+
33
+ context 'with translations' do
34
+ it 'handles translation paths correctly for jsdelivr' do
35
+ instance.cdn = :jsdelivr
36
+ expect(instance.create_cdn_url('ckeditor5', '34.1.0', 'translations/pl.js'))
37
+ .to eq('https://cdn.jsdelivr.net/npm/ckeditor5@34.1.0/dist/translations/pl.js')
38
+ end
39
+
40
+ it 'handles translation paths correctly for unpkg' do
41
+ instance.cdn = :unpkg
42
+ expect(instance.create_cdn_url('ckeditor5', '34.1.0', 'translations/pl.js'))
43
+ .to eq('https://unpkg.com/ckeditor5@34.1.0/dist/translations/pl.js')
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'with commercial CDNs' do
49
+ it_behaves_like 'a CDN provider', :cloud,
50
+ 'https://cdn.ckeditor.com/ckeditor5/%<version>s/ckeditor.js'
51
+
52
+ it 'generates correct CKBox URL' do
53
+ instance.cdn = :ckbox
54
+ expect(instance.create_cdn_url('ckbox', '34.1.0', 'ckeditor.js'))
55
+ .to eq('https://cdn.ckbox.io/ckbox/34.1.0/ckeditor.js')
56
+ end
57
+ end
58
+
59
+ context 'with invalid configuration' do
60
+ it 'raises ArgumentError for unknown CDN provider' do
61
+ instance.cdn = :invalid_cdn
62
+ expect { instance.create_cdn_url('ckeditor5', '34.1.0', 'ckeditor.js') }
63
+ .to raise_error(ArgumentError, 'Unknown provider: invalid_cdn')
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Semver do
6
+ describe '#initialize' do
7
+ context 'with valid version string' do
8
+ it 'accepts version in x.y.z format' do
9
+ expect { described_class.new('1.2.3') }.not_to raise_error
10
+ end
11
+ end
12
+
13
+ context 'with invalid version' do
14
+ it 'raises error for numeric input' do
15
+ expect { described_class.new(123) }
16
+ .to raise_error(ArgumentError, 'invalid version format')
17
+ end
18
+
19
+ it 'raises error for invalid string format' do
20
+ invalid_versions = ['1', '1.2', '1.2.3.4', 'x.y.z', '1.2.x', '1.a.3']
21
+
22
+ invalid_versions.each do |version|
23
+ expect { described_class.new(version) }
24
+ .to raise_error(ArgumentError, 'invalid version format')
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#to_s' do
31
+ it 'returns the version string' do
32
+ version = '1.2.3'
33
+ semver = described_class.new(version)
34
+ expect(semver.to_s).to eq(version)
35
+ end
36
+ end
37
+
38
+ describe '#version' do
39
+ it 'returns the version string' do
40
+ version = '1.2.3'
41
+ semver = described_class.new(version)
42
+ expect(semver.version).to eq(version)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ require 'simplecov_json_formatter'
5
+
6
+ SimpleCov.start do
7
+ enable_coverage :branch
8
+ enable_coverage :line
9
+
10
+ add_filter 'sandbox/'
11
+ add_group 'Library', 'lib/'
12
+
13
+ track_files 'lib/**/*.rb'
14
+
15
+ # minimum_coverage 90
16
+ # minimum_coverage_by_file 80
17
+
18
+ formatters = [
19
+ SimpleCov::Formatter::HTMLFormatter,
20
+ SimpleCov::Formatter::JSONFormatter
21
+ ]
22
+
23
+ formatter SimpleCov::Formatter::MultiFormatter.new(formatters)
24
+ end
25
+
26
+ ENV['RAILS_ENV'] ||= 'test'
27
+
28
+ require File.expand_path('../sandbox/config/application', __dir__)
29
+
30
+ require 'pry'
31
+ require 'spec_helper'
32
+ require 'rspec/rails'
33
+ require 'rspec/expectations'
34
+
35
+ Rails.application.initialize!
36
+
37
+ RSpec.configure do |config|
38
+ config.expect_with :rspec do |expectations|
39
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
40
+ end
41
+
42
+ config.mock_with :rspec do |mocks|
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ config.shared_context_metadata_behavior = :apply_to_host_groups
47
+ config.filter_run_when_matching :focus
48
+ config.disable_monkey_patching!
49
+
50
+ config.default_formatter = 'doc' if config.files_to_run.one?
51
+ config.profile_examples = 10
52
+
53
+ config.order = :random
54
+
55
+ Kernel.rand config.seed
56
+
57
+ config.before(:each) do
58
+ Rails.application.load_seed
59
+ end
60
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiล„ski
@@ -57,8 +57,9 @@ files:
57
57
  - lib/ckeditor5/rails/cdn/url_generator.rb
58
58
  - lib/ckeditor5/rails/context/helpers.rb
59
59
  - lib/ckeditor5/rails/context/props.rb
60
- - lib/ckeditor5/rails/editor/config_helpers.rb
61
60
  - lib/ckeditor5/rails/editor/helpers.rb
61
+ - lib/ckeditor5/rails/editor/helpers/config_helpers.rb
62
+ - lib/ckeditor5/rails/editor/helpers/editor_helpers.rb
62
63
  - lib/ckeditor5/rails/editor/props.rb
63
64
  - lib/ckeditor5/rails/editor/props_inline_plugin.rb
64
65
  - lib/ckeditor5/rails/editor/props_plugin.rb
@@ -66,11 +67,16 @@ files:
66
67
  - lib/ckeditor5/rails/helpers.rb
67
68
  - lib/ckeditor5/rails/hooks/form.rb
68
69
  - lib/ckeditor5/rails/hooks/simple_form.rb
70
+ - lib/ckeditor5/rails/plugins/simple_upload_adapter.rb
69
71
  - lib/ckeditor5/rails/presets/manager.rb
72
+ - lib/ckeditor5/rails/presets/plugins_builder.rb
70
73
  - lib/ckeditor5/rails/presets/preset_builder.rb
71
74
  - lib/ckeditor5/rails/presets/toolbar_builder.rb
72
75
  - lib/ckeditor5/rails/semver.rb
73
76
  - lib/ckeditor5/rails/version.rb
77
+ - spec/lib/ckeditor5/rails/cdn/url_generator_spec.rb
78
+ - spec/lib/ckeditor5/rails/semver_spec.rb
79
+ - spec/spec_helper.rb
74
80
  homepage: https://github.com/Mati365/ckeditor5-rails
75
81
  licenses:
76
82
  - GPL-2.0
@@ -94,4 +100,7 @@ rubygems_version: 3.5.22
94
100
  signing_key:
95
101
  specification_version: 4
96
102
  summary: CKEditor 5 for Rails
97
- test_files: []
103
+ test_files:
104
+ - spec/lib/ckeditor5/rails/cdn/url_generator_spec.rb
105
+ - spec/lib/ckeditor5/rails/semver_spec.rb
106
+ - spec/spec_helper.rb
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CKEditor5::Rails
4
- module Editor::ConfigHelpers
5
- def ckeditor5_element_ref(selector)
6
- { '$element': selector }
7
- end
8
- end
9
- end