ultimate_turbo_modal 2.2.1 → 2.2.2

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: f48f99a3169d751eb02f9efe0f357aa70b285097e03438e2d1b27d1693b9b59c
4
- data.tar.gz: c333e8c524f3c2967d1ddc484aab887165f574e0969bdd1b1aa6d1a48752b7ba
3
+ metadata.gz: 8076c3d9802abd28ef2ff1858b7268f730455c69a214dfb2f8e04d3695c8f68f
4
+ data.tar.gz: fa211158221a41661229457a63874690eac7e07fb19159045362eb0341d4ccee
5
5
  SHA512:
6
- metadata.gz: be4aa9c324429196062c98fd8845b484eb2f14f76e96ee2581063b05568f287e3e4f1961e55a5e6d11a2043a3785af13ebaaf1d9f6594abeb3750f785621f631
7
- data.tar.gz: aaad99d25ccbd2bceb518a1a85ecf2e28a7e768e61ff478fddfc723aad170ef06cd7f325fc012e405008799f43f90600440cff93e29528c81cfcbb07b8c94184
6
+ metadata.gz: 9414a16b2a0de555727b258403230d5a65564e1158b95c61904cca0fd0b9280fe5433eaeb156962b1dbb1bbcdfe25a86d1ec7b1a65c03209211e5b97abb2fde7
7
+ data.tar.gz: 783c9c2af43c3b31e8e974642545ac094e2c5faf1925c84947186e215ba809daac4a6236afa7fc1e24768379f1a12376515982a48cd08350274a9a8a2230fc7b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [2.2.2] - 2026-03-12
2
+
3
+ - Added `close` function on Stimulus Controller. Thanks @bendangelo
4
+ - Focus bug fix. Thanks @pasl
5
+ - Refactor generator and fix [bug #33](https://github.com/cmer/ultimate_turbo_modal/issues/33).
6
+
1
7
  ## [2.2.1] - 2025-08-08
2
8
 
3
9
  - Added `rails generate ultimate_turbo_modal:update` for easy updates
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ultimate_turbo_modal (2.2.1)
4
+ ultimate_turbo_modal (2.2.2)
5
5
  actionpack
6
6
  activesupport
7
7
  phlex-rails
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.1
1
+ 2.2.2
@@ -96,6 +96,10 @@ export default class extends Controller {
96
96
  this.hideModal();
97
97
  }
98
98
 
99
+ close() {
100
+ this.hideModal();
101
+ }
102
+
99
103
  refreshPage() {
100
104
  window.Turbo.visit(window.location.href, { action: "replace" });
101
105
  }
@@ -178,7 +182,7 @@ export default class extends Controller {
178
182
  initialFocus: () => {
179
183
  // Try to focus the first focusable element, or the modal itself
180
184
  const firstFocusable = this.contentTarget.querySelector(
181
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
185
+ 'button:not([tabindex="-1"]), [href]:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])'
182
186
  );
183
187
  return firstFocusable || this.contentTarget;
184
188
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate_turbo_modal",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "The ultimate Turbo / Stimulus / Hotwire modal window for Rails",
5
5
  "main": "dist/ultimate_turbo_modal.min.js",
6
6
  "module": "dist/ultimate_turbo_modal.min.js",
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "pathname"
5
+
6
+ module UltimateTurboModal
7
+ module Generators
8
+ class Base < Rails::Generators::Base
9
+ protected
10
+
11
+ def package_name
12
+ "ultimate_turbo_modal"
13
+ end
14
+
15
+ # Add JS dependency (for install flow)
16
+ def add_js_dependency
17
+ say "Attempting to set up JavaScript dependencies...", :yellow
18
+
19
+ version_spec = "#{package_name}@#{UltimateTurboModal::VERSION}"
20
+
21
+ if uses_importmaps?
22
+ say "Detected Importmaps. Pinning #{version_spec}...", :green
23
+ run "bin/importmap pin #{version_spec}"
24
+ say "✅ Pinned '#{package_name}' via importmap.", :green
25
+ return
26
+ end
27
+
28
+ if uses_javascript_bundler?
29
+ say "Detected jsbundling-rails (Yarn/npm/Bun). Adding #{package_name} package...", :green
30
+ if uses_yarn?
31
+ run "yarn add #{version_spec}"
32
+ say "✅ Added '#{package_name}' using Yarn.", :green
33
+ elsif uses_npm?
34
+ run "npm install --save #{version_spec}"
35
+ say "✅ Added '#{package_name}' using npm.", :green
36
+ elsif uses_bun?
37
+ run "bun add #{version_spec}"
38
+ say "✅ Added '#{package_name}' using Bun.", :green
39
+ else
40
+ say "Attempting to add with Yarn. If you use npm or Bun, please add manually.", :yellow
41
+ run "yarn add #{version_spec}"
42
+ say "If this failed or you use npm/bun, please run:", :yellow
43
+ say "npm install --save #{version_spec}", :cyan
44
+ say "# or", :cyan
45
+ say "bun add #{version_spec}", :cyan
46
+ end
47
+ else
48
+ say "Could not automatically detect Importmaps or jsbundling-rails.", :yellow
49
+ say "Please manually add the '#{package_name}' JavaScript package.", :yellow
50
+ say "If using Importmaps: bin/importmap pin #{version_spec}", :cyan
51
+ say "If using Yarn: yarn add #{version_spec}", :cyan
52
+ say "If using npm: npm install --save #{version_spec}", :cyan
53
+ say "If using Bun: bun add #{version_spec}", :cyan
54
+ say "Then, import it in your app/javascript/application.js:", :yellow
55
+ say "import '#{package_name}'", :cyan
56
+ end
57
+ end
58
+
59
+ # Install all JS dependencies (for update flow)
60
+ def install_all_js_dependencies
61
+ if uses_importmaps?
62
+ version_spec = "#{package_name}@#{UltimateTurboModal::VERSION}"
63
+ say "Detected Importmaps. Ensuring pin for #{version_spec}...", :green
64
+ run "bin/importmap pin #{version_spec}"
65
+ say "✅ Pinned '#{package_name}' via importmap.", :green
66
+ return
67
+ end
68
+
69
+ unless uses_javascript_bundler?
70
+ say "Could not detect Importmaps or jsbundling-rails. Skipping JS install step.", :yellow
71
+ return
72
+ end
73
+
74
+ say "Installing JavaScript dependencies...", :yellow
75
+ if uses_yarn?
76
+ run "yarn install"
77
+ say "✅ Installed dependencies with Yarn.", :green
78
+ elsif uses_npm?
79
+ run "npm install"
80
+ say "✅ Installed dependencies with npm.", :green
81
+ elsif uses_bun?
82
+ run "bun install"
83
+ say "✅ Installed dependencies with Bun.", :green
84
+ else
85
+ say "Attempting to install with Yarn. If you use npm or Bun, please run the appropriate command.", :yellow
86
+ run "yarn install"
87
+ end
88
+ end
89
+
90
+ def uses_importmaps?
91
+ File.exist?(rails_root_join("config", "importmap.rb"))
92
+ end
93
+
94
+ def uses_javascript_bundler?
95
+ File.exist?(rails_root_join("package.json"))
96
+ end
97
+
98
+ def uses_yarn?
99
+ File.exist?(rails_root_join("yarn.lock"))
100
+ end
101
+
102
+ def uses_npm?
103
+ File.exist?(rails_root_join("package-lock.json")) && !uses_yarn? && !uses_bun?
104
+ end
105
+
106
+ def uses_bun?
107
+ File.exist?(rails_root_join("bun.lockb"))
108
+ end
109
+
110
+ def rails_root_join(*args)
111
+ Pathname.new(destination_root).join(*args)
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
- require "pathname" # Needed for Pathname helper
4
+ require_relative "base"
5
5
 
6
6
  module UltimateTurboModal
7
7
  module Generators
8
- class InstallGenerator < Rails::Generators::Base
8
+ class InstallGenerator < UltimateTurboModal::Generators::Base
9
9
  source_root File.expand_path("templates", __dir__)
10
10
 
11
11
  desc "Installs UltimateTurboModal: copies initializer/flavor, sets up JS, registers Stimulus controller, adds Turbo Frame."
@@ -17,99 +17,61 @@ module UltimateTurboModal
17
17
 
18
18
  # Step 2: Setup Javascript Dependencies (Yarn/npm/Bun or Importmap)
19
19
  def setup_javascript_dependencies
20
- package_name = "ultimate_turbo_modal" # Or the actual npm package name if different
21
-
22
- say "\nAttempting to set up JavaScript dependencies...", :yellow
23
-
24
- if uses_importmaps?
25
- say "Detected Importmaps. Pinning #{package_name}...", :green
26
- run "bin/importmap pin #{package_name}"
27
-
28
- say "\n✅ Pinned '#{package_name}' via importmap.", :green
29
-
30
- elsif uses_javascript_bundler?
31
- say "Detected jsbundling-rails (Yarn/npm/Bun). Adding #{package_name} package...", :green
32
- if uses_yarn?
33
- run "yarn add #{package_name}"
34
- say "\n✅ Added '#{package_name}' using Yarn.", :green
35
- elsif uses_npm?
36
- run "npm install --save #{package_name}"
37
- say "\n✅ Added '#{package_name}' using npm.", :green
38
- elsif uses_bun?
39
- run "bun add #{package_name}"
40
- say "\n✅ Added '#{package_name}' using Bun.", :green
41
- else
42
- # Default or fallback: Try yarn, but provide instructions for others
43
- say "Attempting to add with Yarn. If you use npm or Bun, please add manually.", :yellow
44
- run "yarn add #{package_name}"
45
- say "\n✅ Attempted to add '#{package_name}' using Yarn.", :green
46
- say " If this failed or you use npm/bun, please run:", :yellow
47
- say " npm install --save #{package_name}", :cyan
48
- say " # or", :cyan
49
- say " bun add #{package_name}\n", :cyan
50
- end
51
- else
52
- # Fallback instructions if neither is clearly detected
53
- say "\nCould not automatically detect Importmaps or jsbundling-rails.", :yellow
54
- say "Please manually add the '#{package_name}' JavaScript package.", :yellow
55
- say "If using Importmaps: bin/importmap pin #{package_name}", :cyan
56
- say "If using Yarn: yarn add #{package_name}", :cyan
57
- say "If using npm: npm install --save #{package_name}", :cyan
58
- say "If using Bun: bun add #{package_name}", :cyan
59
- say "Then, import it in your app/javascript/application.js:", :yellow
60
- say "import '#{package_name}'\n", :cyan
61
- end
20
+ add_js_dependency
62
21
  end
63
22
 
64
23
  # Step 3: Register Stimulus Controller
65
24
  def setup_stimulus_controller
66
- stimulus_controller_path = rails_root_join("app", "javascript", "controllers", "index.js")
67
- controller_package = "ultimate_turbo_modal" # Package name where the controller is defined
68
- controller_name = "UltimateTurboModalController" # The exported controller class name
69
- stimulus_identifier = "modal" # The identifier for application.register
25
+ index_path = rails_root_join("app", "javascript", "controllers", "index.js")
26
+ application_path = rails_root_join("app", "javascript", "controllers", "application.js")
27
+ controller_name = "UltimateTurboModalController"
28
+ stimulus_identifier = "modal"
70
29
 
71
- import_line = "import { #{controller_name} } from \"#{controller_package}\"\n"
30
+ import_line = "import { #{controller_name} } from \"#{package_name}\"\n"
72
31
  register_line = "application.register(\"#{stimulus_identifier}\", #{controller_name})\n"
73
32
 
74
- say "\nAttempting to register Stimulus controller in #{stimulus_controller_path}...", :yellow
33
+ # Determine which file contains Application.start() it may be index.js or application.js
34
+ target_path, file_content = find_stimulus_target(index_path, application_path)
35
+
36
+ say "\nAttempting to register Stimulus controller...", :yellow
75
37
 
76
- unless File.exist?(stimulus_controller_path)
77
- say "❌ Stimulus controllers index file not found at #{stimulus_controller_path}.", :red
38
+ unless target_path
39
+ say "❌ Stimulus controllers file not found.", :red
78
40
  say " Please manually add the following lines to your Stimulus setup:", :yellow
79
41
  say " #{import_line.strip}", :cyan
80
42
  say " #{register_line.strip}\n", :cyan
81
- return # Exit this method if the file doesn't exist
43
+ return
82
44
  end
83
45
 
84
- # Read the file content to check if lines already exist
85
- file_content = File.read(stimulus_controller_path)
46
+ say " Target file: #{target_path}", :yellow
86
47
 
87
- # Insert the import statement after the last existing import or a common marker
88
- # Using a regex to find the Stimulus import is often reliable
89
- import_anchor = /import .* from "@hotwired\/stimulus"\n/
90
- if file_content.match?(import_anchor) && !file_content.include?(import_line)
91
- insert_into_file stimulus_controller_path, import_line, after: import_anchor
48
+ # Insert the import statement after an existing import from @hotwired/stimulus or ./application
49
+ import_anchor = /import .* from ["'](?:@hotwired\/stimulus|\.\/application)["']\n/
50
+ if file_content.include?(import_line)
51
+ say "⏩ Import statement already exists.", :blue
52
+ elsif file_content.match?(import_anchor)
53
+ insert_into_file target_path, import_line, after: import_anchor
92
54
  say "✅ Added import statement.", :green
93
- elsif !file_content.include?(import_line)
94
- # Fallback: insert at the beginning if Stimulus import wasn't found (less ideal)
95
- insert_into_file stimulus_controller_path, import_line, before: /import/
55
+ elsif file_content.match?(/import/)
56
+ insert_into_file target_path, import_line, before: /import/
96
57
  say "✅ Added import statement (fallback position).", :green
97
58
  else
98
- say "⏩ Import statement already exists.", :blue
59
+ prepend_to_file target_path, import_line
60
+ say "✅ Added import statement (prepended to file).", :green
99
61
  end
100
62
 
101
-
102
63
  # Insert the register statement after Application.start()
103
- register_anchor = /Application\.start$$$$\n/
104
- if file_content.match?(register_anchor) && !file_content.include?(register_line)
105
- insert_into_file stimulus_controller_path, register_line, after: register_anchor
106
- say "✅ Added controller registration.", :green
107
- elsif !file_content.include?(register_line)
108
- say " Could not find `Application.start()` line to insert registration after.", :red
109
- say " Please manually add this line after your Stimulus application starts:", :yellow
110
- say " #{register_line.strip}\n", :cyan
64
+ register_anchor = /Application\.start\(\)\n/
65
+ if file_content.include?(register_line)
66
+ say "⏩ Controller registration already exists.", :blue
67
+ elsif file_content.match?(register_anchor)
68
+ insert_into_file target_path, register_line, after: register_anchor
69
+ say " Added controller registration.", :green
111
70
  else
112
- say " Controller registration already exists.", :blue
71
+ say " Could not find `Application.start()` line in #{target_path}.", :red
72
+ say " Please manually add these lines to your Stimulus setup:", :yellow
73
+ say " #{import_line.strip}", :cyan
74
+ say " #{register_line.strip}\n", :cyan
113
75
  end
114
76
  end
115
77
 
@@ -196,28 +158,17 @@ module UltimateTurboModal
196
158
  end
197
159
  end
198
160
 
199
- def uses_importmaps?
200
- File.exist?(rails_root_join("config", "importmap.rb"))
201
- end
202
-
203
- def uses_javascript_bundler?
204
- File.exist?(rails_root_join("package.json"))
205
- end
206
-
207
- def uses_yarn?
208
- File.exist?(rails_root_join("yarn.lock"))
209
- end
210
-
211
- def uses_npm?
212
- File.exist?(rails_root_join("package-lock.json")) && !uses_yarn? && !uses_bun?
213
- end
214
-
215
- def uses_bun?
216
- File.exist?(rails_root_join("bun.lockb"))
217
- end
161
+ def find_stimulus_target(index_path, application_path)
162
+ [index_path, application_path].each do |path|
163
+ next unless File.exist?(path)
164
+ content = File.read(path)
165
+ return [path, content] if content.match?(/Application\.start\(\)/)
166
+ end
218
167
 
219
- def rails_root_join(*args)
220
- Pathname.new(destination_root).join(*args)
168
+ # Fall back to index.js even without Application.start()
169
+ if File.exist?(index_path)
170
+ [index_path, File.read(index_path)]
171
+ end
221
172
  end
222
173
  end
223
174
  end
@@ -3,10 +3,11 @@
3
3
  require "rails/generators"
4
4
  require "json"
5
5
  require "pathname"
6
+ require_relative "base"
6
7
 
7
8
  module UltimateTurboModal
8
9
  module Generators
9
- class UpdateGenerator < Rails::Generators::Base
10
+ class UpdateGenerator < UltimateTurboModal::Generators::Base
10
11
  source_root File.expand_path("templates", __dir__)
11
12
 
12
13
  desc "Updates UltimateTurboModal: aligns npm package version to gem version and refreshes the configured flavor initializer."
@@ -29,6 +30,13 @@ module UltimateTurboModal
29
30
  package_name = "ultimate_turbo_modal"
30
31
  new_version = UltimateTurboModal::VERSION.to_s
31
32
 
33
+ # Special case: demo app links to local JS package; never update its version
34
+ if json.dig("dependencies", package_name) == "link:../javascript" ||
35
+ json.dig("devDependencies", package_name) == "link:../javascript"
36
+ say "Detected local link for '#{package_name}' (link:../javascript). Skipping version update.", :blue
37
+ return
38
+ end
39
+
32
40
  updated = false
33
41
 
34
42
  %w[dependencies devDependencies].each do |section|
@@ -49,6 +57,10 @@ module UltimateTurboModal
49
57
  end
50
58
  end
51
59
 
60
+ def install_js_dependencies
61
+ install_all_js_dependencies
62
+ end
63
+
52
64
  def copy_flavor_file
53
65
  flavor = detect_flavor
54
66
  unless flavor
@@ -55,7 +55,7 @@ class UltimateTurboModal::Base < Phlex::HTML
55
55
  modal(&block)
56
56
  end
57
57
  elsif turbo_stream?
58
- Turbo::StreamsHelper.turbo_stream_action_tag("update", target: "modal") do
58
+ turbo_stream_action_tag("update", target: "modal") do
59
59
  modal(&block)
60
60
  end
61
61
  else
@@ -5,6 +5,7 @@ require "phlex/deferred_render_with_main_content"
5
5
  require "ultimate_turbo_modal/configuration"
6
6
  require "ultimate_turbo_modal/railtie"
7
7
  require "ultimate_turbo_modal/base"
8
+ require "generators/ultimate_turbo_modal/base"
8
9
  require "generators/ultimate_turbo_modal/install_generator"
9
10
  require "generators/ultimate_turbo_modal/update_generator"
10
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ultimate_turbo_modal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Mercier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-08 00:00:00.000000000 Z
11
+ date: 2026-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex-rails
@@ -177,6 +177,7 @@ files:
177
177
  - javascript/scripts/update-version.js
178
178
  - javascript/styles/vanilla.css
179
179
  - javascript/yarn.lock
180
+ - lib/generators/ultimate_turbo_modal/base.rb
180
181
  - lib/generators/ultimate_turbo_modal/install_generator.rb
181
182
  - lib/generators/ultimate_turbo_modal/templates/flavors/custom.rb
182
183
  - lib/generators/ultimate_turbo_modal/templates/flavors/tailwind.rb