turbo_turbo 0.2.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 +7 -0
- data/.claude/settings.local.json +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +96 -0
- data/CHANGELOG.md +50 -0
- data/LICENSE +21 -0
- data/README.md +498 -0
- data/Rakefile +12 -0
- data/app/assets/stylesheets/turbo_turbo/alerts.css +7 -0
- data/app/assets/stylesheets/turbo_turbo/base.css +4 -0
- data/app/assets/stylesheets/turbo_turbo/button.css +24 -0
- data/app/assets/stylesheets/turbo_turbo/modal.css +81 -0
- data/app/components/turbo_turbo/alerts/alert_component.html.erb +36 -0
- data/app/components/turbo_turbo/alerts/alert_component.rb +12 -0
- data/app/components/turbo_turbo/alerts/error_component.html.erb +36 -0
- data/app/components/turbo_turbo/alerts/error_component.rb +12 -0
- data/app/components/turbo_turbo/alerts/info_component.html.erb +36 -0
- data/app/components/turbo_turbo/alerts/info_component.rb +12 -0
- data/app/components/turbo_turbo/alerts/success_component.html.erb +47 -0
- data/app/components/turbo_turbo/alerts/success_component.rb +12 -0
- data/app/components/turbo_turbo/alerts/warning_component.html.erb +36 -0
- data/app/components/turbo_turbo/alerts/warning_component.rb +12 -0
- data/app/components/turbo_turbo/modal_component.html.erb +20 -0
- data/app/components/turbo_turbo/modal_component.rb +9 -0
- data/app/components/turbo_turbo/modal_footer_component.html.erb +6 -0
- data/app/components/turbo_turbo/modal_footer_component.rb +10 -0
- data/config/locales/en.yml +15 -0
- data/config/routes/turbo_turbo_routes.rb +20 -0
- data/lib/generators/turbo_turbo/install_generator.rb +351 -0
- data/lib/generators/turbo_turbo/layout_generator.rb +221 -0
- data/lib/generators/turbo_turbo/templates/config/routes/turbo_turbo_routes.rb +20 -0
- data/lib/generators/turbo_turbo/templates/turbo_turbo/_error_message.html.erb +15 -0
- data/lib/generators/turbo_turbo/templates/turbo_turbo/_flashes.html.erb +8 -0
- data/lib/generators/turbo_turbo/templates/turbo_turbo/_modal_background.html.erb +2 -0
- data/lib/generators/turbo_turbo/templates/turbo_turbo/flash_controller.js +28 -0
- data/lib/generators/turbo_turbo/templates/turbo_turbo/modal_controller.js +114 -0
- data/lib/generators/turbo_turbo/views_generator.rb +57 -0
- data/lib/turbo_turbo/controller_helpers.rb +157 -0
- data/lib/turbo_turbo/engine.rb +19 -0
- data/lib/turbo_turbo/form_helper.rb +135 -0
- data/lib/turbo_turbo/parameter_sanitizer.rb +69 -0
- data/lib/turbo_turbo/standard_actions.rb +96 -0
- data/lib/turbo_turbo/test_helpers.rb +64 -0
- data/lib/turbo_turbo/version.rb +5 -0
- data/lib/turbo_turbo.rb +15 -0
- data/sig/turbo_turbo.rbs +4 -0
- data/turbo_turbo.gemspec +42 -0
- metadata +136 -0
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module TurboTurbo
|
7
|
+
module Generators
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
9
|
+
desc "Install TurboTurbo files"
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
11
|
+
|
12
|
+
def install_javascript_controllers
|
13
|
+
empty_directory "app/javascript/controllers/turbo_turbo"
|
14
|
+
copy_file "turbo_turbo/modal_controller.js", "app/javascript/controllers/turbo_turbo/modal_controller.js"
|
15
|
+
copy_file "turbo_turbo/flash_controller.js", "app/javascript/controllers/turbo_turbo/flash_controller.js"
|
16
|
+
end
|
17
|
+
|
18
|
+
def register_stimulus_controllers
|
19
|
+
index_file = "app/javascript/controllers/index.js"
|
20
|
+
|
21
|
+
unless File.exist?(index_file)
|
22
|
+
say "Warning: Could not find #{index_file}", :yellow
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
content = File.read(index_file)
|
27
|
+
|
28
|
+
# Check if TurboTurbo controllers are already registered
|
29
|
+
if content.include?("turbo-turbo--modal") && content.include?("turbo-turbo--flash")
|
30
|
+
say "TurboTurbo controllers already registered in #{index_file}", :blue
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add import statements and registrations
|
35
|
+
turbo_turbo_imports = <<~JS
|
36
|
+
|
37
|
+
// TurboTurbo controllers
|
38
|
+
import TurboTurboModalController from "./turbo_turbo/modal_controller"
|
39
|
+
import TurboTurboFlashController from "./turbo_turbo/flash_controller"
|
40
|
+
|
41
|
+
application.register("turbo-turbo--modal", TurboTurboModalController)
|
42
|
+
application.register("turbo-turbo--flash", TurboTurboFlashController)
|
43
|
+
JS
|
44
|
+
|
45
|
+
# Insert before the last line (usually eagerLoadControllersFrom)
|
46
|
+
lines = content.split("\n")
|
47
|
+
insert_index = lines.rindex { |line| line.strip.length.positive? && !line.strip.start_with?("//") }
|
48
|
+
|
49
|
+
if insert_index
|
50
|
+
lines.insert(insert_index + 1, turbo_turbo_imports)
|
51
|
+
File.write(index_file, lines.join("\n"))
|
52
|
+
say "Added TurboTurbo controller registrations to #{index_file}", :green
|
53
|
+
else
|
54
|
+
say "Warning: Could not determine where to insert TurboTurbo registrations in #{index_file}", :yellow
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def install_layout_files
|
59
|
+
empty_directory "app/views/turbo_turbo"
|
60
|
+
copy_file "turbo_turbo/_flashes.html.erb", "app/views/turbo_turbo/_flashes.html.erb"
|
61
|
+
copy_file "turbo_turbo/_modal_background.html.erb", "app/views/turbo_turbo/_modal_background.html.erb"
|
62
|
+
copy_file "turbo_turbo/_error_message.html.erb", "app/views/turbo_turbo/_error_message.html.erb"
|
63
|
+
end
|
64
|
+
|
65
|
+
def modify_application_controller
|
66
|
+
controller_file = "app/controllers/application_controller.rb"
|
67
|
+
|
68
|
+
unless File.exist?(controller_file)
|
69
|
+
say "Warning: Could not find #{controller_file}", :yellow
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
content = File.read(controller_file)
|
74
|
+
original_content = content.dup
|
75
|
+
|
76
|
+
# Add include TurboTurbo::ControllerHelpers if not present
|
77
|
+
unless content.include?("include TurboTurbo::ControllerHelpers")
|
78
|
+
content = content.gsub(/(class ApplicationController < ActionController::Base\s*\n)/) do
|
79
|
+
"#{::Regexp.last_match(1)} include TurboTurbo::ControllerHelpers\n\n"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Add flash types if not present
|
84
|
+
unless content.include?("add_flash_types") || content.match?(/add_flash_types\s*:success.*:error.*:warning.*:info/)
|
85
|
+
content = content.gsub(/(include TurboTurbo::ControllerHelpers\s*\n)/) do
|
86
|
+
"#{::Regexp.last_match(1)} add_flash_types :success, :error, :warning, :info\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if content == original_content
|
91
|
+
say "#{controller_file} already includes TurboTurbo configuration", :blue
|
92
|
+
else
|
93
|
+
File.write(controller_file, content)
|
94
|
+
say "Modified #{controller_file} to include TurboTurbo helpers and flash types", :green
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_css_import
|
99
|
+
copy_css_file_to_app
|
100
|
+
add_css_import_to_main_file
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def copy_css_file_to_app
|
106
|
+
# Copy the TurboTurbo CSS files from the gem to the app
|
107
|
+
gem_css_dir = File.join(gem_root, "app/assets/stylesheets")
|
108
|
+
app_css_dir = "app/assets/stylesheets"
|
109
|
+
|
110
|
+
# CSS subdirectory (now contains base.css and component CSS files)
|
111
|
+
gem_css_subdir = File.join(gem_css_dir, "turbo_turbo")
|
112
|
+
app_css_subdir = File.join(app_css_dir, "turbo_turbo")
|
113
|
+
|
114
|
+
unless File.exist?(gem_css_subdir)
|
115
|
+
say "Warning: Could not find turbo_turbo CSS directory in the gem", :red
|
116
|
+
return
|
117
|
+
end
|
118
|
+
|
119
|
+
if File.exist?(app_css_subdir)
|
120
|
+
say "TurboTurbo CSS directory already exists in #{app_css_dir}", :blue
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
# Copy CSS subdirectory (contains base.css and component files)
|
125
|
+
FileUtils.cp_r(gem_css_subdir, app_css_subdir)
|
126
|
+
|
127
|
+
say "Copied TurboTurbo CSS files to #{app_css_dir}/turbo_turbo", :green
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_css_import_to_main_file
|
131
|
+
css_files = [
|
132
|
+
"app/assets/stylesheets/application.tailwind.css",
|
133
|
+
"app/assets/stylesheets/application.css"
|
134
|
+
]
|
135
|
+
|
136
|
+
css_file = css_files.find { |file| File.exist?(file) }
|
137
|
+
|
138
|
+
unless css_file
|
139
|
+
say "Warning: Could not find application.tailwind.css or application.css", :red
|
140
|
+
say "Please manually add '@import \"./turbo_turbo.css\";' to your main CSS file", :red
|
141
|
+
return
|
142
|
+
end
|
143
|
+
|
144
|
+
content = File.read(css_file)
|
145
|
+
|
146
|
+
# Check if import already exists (check for both old and new import formats)
|
147
|
+
if content.include?('@import "turbo_turbo"') || content.include?('@import "./turbo_turbo"') || content.include?('@import "./turbo_turbo.css"') || content.include?('@import "./turbo_turbo/base"')
|
148
|
+
say "TurboTurbo CSS import already exists in #{css_file}", :blue
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
# Add import at the top of the file (using relative path to base.css)
|
153
|
+
content = "@import \"./turbo_turbo/base\";\n\n#{content}"
|
154
|
+
File.write(css_file, content)
|
155
|
+
say "Added TurboTurbo CSS import to #{css_file}", :green
|
156
|
+
end
|
157
|
+
|
158
|
+
def gem_root
|
159
|
+
@gem_root ||= File.expand_path("../../..", __dir__)
|
160
|
+
end
|
161
|
+
|
162
|
+
def modify_layout_file
|
163
|
+
layout_files = [
|
164
|
+
"app/views/layouts/application.html.erb",
|
165
|
+
"app/views/layouts/application.html.slim"
|
166
|
+
]
|
167
|
+
|
168
|
+
layout_file = layout_files.find { |file| File.exist?(file) }
|
169
|
+
|
170
|
+
unless layout_file
|
171
|
+
say "Warning: Could not find application layout file", :yellow
|
172
|
+
return
|
173
|
+
end
|
174
|
+
|
175
|
+
content = File.read(layout_file)
|
176
|
+
original_content = content.dup
|
177
|
+
|
178
|
+
content = if layout_file.end_with?(".slim")
|
179
|
+
modify_slim_layout(content)
|
180
|
+
else
|
181
|
+
modify_erb_layout(content)
|
182
|
+
end
|
183
|
+
|
184
|
+
if content == original_content
|
185
|
+
say "#{layout_file} already includes TurboTurbo components", :blue
|
186
|
+
else
|
187
|
+
File.write(layout_file, content)
|
188
|
+
say "Modified #{layout_file} to include TurboTurbo components", :green
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def install_routes
|
193
|
+
# Copy routes file to config/routes/
|
194
|
+
empty_directory "config/routes"
|
195
|
+
copy_file "config/routes/turbo_turbo_routes.rb", "config/routes/turbo_turbo_routes.rb"
|
196
|
+
|
197
|
+
# Add draw(:turbo_turbo_routes) to main routes.rb
|
198
|
+
routes_file = "config/routes.rb"
|
199
|
+
|
200
|
+
unless File.exist?(routes_file)
|
201
|
+
say "Warning: Could not find #{routes_file}", :yellow
|
202
|
+
return
|
203
|
+
end
|
204
|
+
|
205
|
+
content = File.read(routes_file)
|
206
|
+
|
207
|
+
# Check if draw(:turbo_turbo_routes) already exists
|
208
|
+
if content.include?("draw(:turbo_turbo_routes)") || content.include?('draw("turbo_turbo_routes")')
|
209
|
+
say "TurboTurbo routes already included in #{routes_file}", :blue
|
210
|
+
return
|
211
|
+
end
|
212
|
+
|
213
|
+
# Add draw(:turbo_turbo_routes) before the final 'end'
|
214
|
+
content = content.gsub(/(\s*end\s*)$/) do
|
215
|
+
" draw(:turbo_turbo_routes)\n#{::Regexp.last_match(1)}"
|
216
|
+
end
|
217
|
+
|
218
|
+
File.write(routes_file, content)
|
219
|
+
say "Added draw(:turbo_turbo_routes) to #{routes_file}", :green
|
220
|
+
end
|
221
|
+
|
222
|
+
def display_instructions
|
223
|
+
say "\n\nTurboTurbo has been installed!", :green
|
224
|
+
say "\nWhat was installed:"
|
225
|
+
say "✅ JavaScript controllers copied to app/javascript/controllers/turbo_turbo/"
|
226
|
+
say "✅ Stimulus controllers registered in index.js"
|
227
|
+
say "✅ ApplicationController configured with TurboTurbo::ControllerHelpers"
|
228
|
+
say "✅ Flash types configured (:success, :error, :warning, :info)"
|
229
|
+
say "✅ CSS files copied to app/assets/stylesheets/turbo_turbo/"
|
230
|
+
say "✅ CSS import added to main stylesheet"
|
231
|
+
say "✅ Body tag configured with turbo-turbo--modal controller"
|
232
|
+
say "✅ Flash messages render added"
|
233
|
+
say "✅ Modal background render added"
|
234
|
+
say "✅ TurboTurbo::ModalComponent render added"
|
235
|
+
say "✅ TurboTurbo routes template copied to config/routes/turbo_turbo_routes.rb"
|
236
|
+
say "✅ draw(:turbo_turbo_routes) added to config/routes.rb"
|
237
|
+
say "\nNext steps:"
|
238
|
+
say "🚀 You're ready to use TurboTurbo! Start by adding turbo_actions to your controllers."
|
239
|
+
say "📝 Add your modal routes to config/routes/turbo_turbo_routes.rb"
|
240
|
+
say "\nOptional:"
|
241
|
+
say "• Run 'rails generate turbo_turbo:views' to copy ViewComponents for customization"
|
242
|
+
say "• Run 'rails generate turbo_turbo:layout [LAYOUT_NAME]' for custom layouts"
|
243
|
+
say "\nFor usage examples, see: https://github.com/lordofthedanse/turbo_turbo"
|
244
|
+
end
|
245
|
+
|
246
|
+
def modify_erb_layout(content)
|
247
|
+
# Add turbo-turbo--modal to data-controller if not present
|
248
|
+
if content.match?(/data-controller\s*=\s*["'][^"']*["']/)
|
249
|
+
# data-controller exists, check if turbo-turbo--modal is present
|
250
|
+
unless content.match?(/data-controller\s*=\s*["'][^"']*\bturbo-turbo--modal\b[^"']*["']/)
|
251
|
+
content = content.gsub(/(data-controller\s*=\s*["'])([^"']*)(['"])/) do
|
252
|
+
prefix = ::Regexp.last_match(1)
|
253
|
+
controller_list = ::Regexp.last_match(2)
|
254
|
+
suffix = ::Regexp.last_match(3)
|
255
|
+
controllers = controller_list.strip.split(/\s+/)
|
256
|
+
controllers << "turbo-turbo--modal" unless controllers.include?("turbo-turbo--modal")
|
257
|
+
"#{prefix}#{controllers.join(' ')}#{suffix}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
else
|
261
|
+
# Add data-controller with turbo-turbo--modal to body tag
|
262
|
+
content = content.gsub(/(<body[^>]*?)>/) do
|
263
|
+
body_tag = ::Regexp.last_match(1)
|
264
|
+
"#{body_tag} data-controller=\"turbo-turbo--modal\">"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Add layout renders after body tag if not present
|
269
|
+
unless content.include?("render 'turbo_turbo/flashes'") || content.include?('render "turbo_turbo/flashes"')
|
270
|
+
content = content.gsub(/(<body[^>]*>\s*)/) do
|
271
|
+
"#{::Regexp.last_match(1)} <%= render 'turbo_turbo/flashes' %>\n "
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
unless content.include?("render 'turbo_turbo/modal_background'") || content.include?('render "turbo_turbo/modal_background"')
|
276
|
+
content = content.gsub(%r{(.*render ['"]turbo_turbo/flashes['"].*\n\s*)}) do
|
277
|
+
"#{::Regexp.last_match(1)} <%= render 'turbo_turbo/modal_background' %>\n "
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Add ModalComponent before closing body tag if not present
|
282
|
+
unless content.include?("TurboTurbo::ModalComponent")
|
283
|
+
content = content.gsub(%r{(\s*)</body>}) do
|
284
|
+
"#{::Regexp.last_match(1)} <%= render TurboTurbo::ModalComponent.new %>\n#{::Regexp.last_match(1)}</body>"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
content
|
289
|
+
end
|
290
|
+
|
291
|
+
def modify_slim_layout(content)
|
292
|
+
# Add turbo-turbo--modal to data-controller if not present
|
293
|
+
if content.match?(/data-controller\s*=\s*["'][^"']*["']/)
|
294
|
+
# data-controller exists, check if turbo-turbo--modal is present
|
295
|
+
unless content.match?(/data-controller\s*=\s*["'][^"']*\bturbo-turbo--modal\b[^"']*["']/)
|
296
|
+
content = content.gsub(/(data-controller\s*=\s*["'])([^"']*)(['"])/) do
|
297
|
+
prefix = ::Regexp.last_match(1)
|
298
|
+
controller_list = ::Regexp.last_match(2)
|
299
|
+
suffix = ::Regexp.last_match(3)
|
300
|
+
controllers = controller_list.strip.split(/\s+/)
|
301
|
+
controllers << "turbo-turbo--modal" unless controllers.include?("turbo-turbo--modal")
|
302
|
+
"#{prefix}#{controllers.join(' ')}#{suffix}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
else
|
306
|
+
# Add data-controller with turbo-turbo--modal to body tag
|
307
|
+
content = content.gsub(/(^ *body(?:\.[a-zA-Z0-9_-]+)*)\s*$/m) do
|
308
|
+
body_line = ::Regexp.last_match(1)
|
309
|
+
"#{body_line} data-controller=\"turbo-turbo--modal\""
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Add layout renders after body tag if not present
|
314
|
+
unless content.match?(%r{render\s+["']turbo_turbo/flashes["']}) || content.match?(%r{render\s+"turbo_turbo/flashes"})
|
315
|
+
content = content.gsub(/(^ *body(?:\.[a-zA-Z0-9_-]+)*.*\n)/m) do
|
316
|
+
"#{::Regexp.last_match(1)} = render \"turbo_turbo/flashes\"\n"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
unless content.match?(%r{render\s+["']turbo_turbo/modal_background["']}) || content.match?(%r{render\s+"turbo_turbo/modal_background"})
|
321
|
+
content = content.gsub(%r{(.*render\s+["']turbo_turbo/flashes["'].*\n)}) do
|
322
|
+
"#{::Regexp.last_match(1)} = render \"turbo_turbo/modal_background\"\n"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Add TurboTurbo::ModalComponent at end of body if not present
|
327
|
+
unless content.include?("TurboTurbo::ModalComponent")
|
328
|
+
# Find the last line with content before implicit body closing
|
329
|
+
lines = content.split("\n")
|
330
|
+
body_found = false
|
331
|
+
insert_index = -1
|
332
|
+
|
333
|
+
lines.each_with_index do |line, index|
|
334
|
+
if line.match?(/^\s*body/)
|
335
|
+
body_found = true
|
336
|
+
elsif body_found && line.strip.length.positive? && !line.match?(%r{^\s*/}) # not a comment
|
337
|
+
insert_index = index
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
if insert_index >= 0
|
342
|
+
lines.insert(insert_index + 1, " = render TurboTurbo::ModalComponent.new")
|
343
|
+
content = lines.join("\n")
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
content
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
|
5
|
+
module TurboTurbo
|
6
|
+
module Generators
|
7
|
+
class LayoutGenerator < Rails::Generators::Base
|
8
|
+
desc "Install TurboTurbo components to a custom layout file"
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
10
|
+
|
11
|
+
argument :layout_name, type: :string, default: "application",
|
12
|
+
desc: "The layout file name (without extension)"
|
13
|
+
|
14
|
+
class_option :skip_flash, type: :boolean, default: false,
|
15
|
+
desc: "Skip adding flash-related components (flashes render)"
|
16
|
+
|
17
|
+
class_option :skip_modal, type: :boolean, default: false,
|
18
|
+
desc: "Skip adding modal-related components (data controller, modal background, ModalComponent)"
|
19
|
+
|
20
|
+
def check_if_everything_skipped
|
21
|
+
return unless options[:skip_flash] && options[:skip_modal]
|
22
|
+
|
23
|
+
say "You skipped everything, so we did nothing!", :yellow
|
24
|
+
exit(0)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ensure_layout_partials_exist
|
28
|
+
empty_directory "app/views/turbo_turbo"
|
29
|
+
|
30
|
+
if !options[:skip_flash] && !File.exist?("app/views/turbo_turbo/_flashes.html.erb")
|
31
|
+
copy_file "turbo_turbo/_flashes.html.erb", "app/views/turbo_turbo/_flashes.html.erb"
|
32
|
+
end
|
33
|
+
|
34
|
+
return if options[:skip_modal]
|
35
|
+
return if File.exist?("app/views/turbo_turbo/_modal_background.html.erb")
|
36
|
+
|
37
|
+
copy_file "turbo_turbo/_modal_background.html.erb", "app/views/turbo_turbo/_modal_background.html.erb"
|
38
|
+
end
|
39
|
+
|
40
|
+
def modify_custom_layout_file
|
41
|
+
layout_files = [
|
42
|
+
"app/views/layouts/#{layout_name}.html.erb",
|
43
|
+
"app/views/layouts/#{layout_name}.html.slim"
|
44
|
+
]
|
45
|
+
|
46
|
+
layout_file = layout_files.find { |file| File.exist?(file) }
|
47
|
+
|
48
|
+
unless layout_file
|
49
|
+
say "Error: Could not find layout file for '#{layout_name}'", :red
|
50
|
+
say "Looking for: #{layout_files.join(' or ')}", :yellow
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
content = File.read(layout_file)
|
55
|
+
original_content = content.dup
|
56
|
+
|
57
|
+
content = if layout_file.end_with?(".slim")
|
58
|
+
modify_slim_layout(content)
|
59
|
+
else
|
60
|
+
modify_erb_layout(content)
|
61
|
+
end
|
62
|
+
|
63
|
+
if content == original_content
|
64
|
+
say "#{layout_file} already includes TurboTurbo components", :blue
|
65
|
+
else
|
66
|
+
File.write(layout_file, content)
|
67
|
+
say "Modified #{layout_file} to include TurboTurbo components", :green
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def display_completion_message
|
72
|
+
say "\nTurboTurbo components added to #{layout_name} layout!", :green
|
73
|
+
say "\nComponents added:"
|
74
|
+
|
75
|
+
say "✅ turbo_turbo/flashes render" unless options[:skip_flash]
|
76
|
+
|
77
|
+
unless options[:skip_modal]
|
78
|
+
say "✅ turbo-turbo--modal data controller"
|
79
|
+
say "✅ turbo_turbo/modal_background render"
|
80
|
+
say "✅ TurboTurbo::ModalComponent render"
|
81
|
+
end
|
82
|
+
|
83
|
+
return unless options[:skip_flash] || options[:skip_modal]
|
84
|
+
|
85
|
+
say "\nSkipped components:"
|
86
|
+
say "⏭️ Flash-related: turbo_turbo/flashes render" if options[:skip_flash]
|
87
|
+
return unless options[:skip_modal]
|
88
|
+
|
89
|
+
say "⏭️ Modal-related: data controller, modal background, ModalComponent"
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def modify_erb_layout(content)
|
95
|
+
# Add turbo-turbo--modal to data-controller if not present and not skipped
|
96
|
+
unless options[:skip_modal]
|
97
|
+
if content.match?(/data-controller\s*=\s*["'][^"']*["']/)
|
98
|
+
# data-controller exists, check if turbo-turbo--modal is present
|
99
|
+
unless content.match?(/data-controller\s*=\s*["'][^"']*\bturbo-turbo--modal\b[^"']*["']/)
|
100
|
+
content = content.gsub(/(data-controller\s*=\s*["'])([^"']*)(['"])/) do
|
101
|
+
prefix = ::Regexp.last_match(1)
|
102
|
+
controller_list = ::Regexp.last_match(2)
|
103
|
+
suffix = ::Regexp.last_match(3)
|
104
|
+
controllers = controller_list.strip.split(/\s+/)
|
105
|
+
controllers << "turbo-turbo--modal" unless controllers.include?("turbo-turbo--modal")
|
106
|
+
"#{prefix}#{controllers.join(' ')}#{suffix}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# Add data-controller with turbo-turbo--modal to body tag
|
111
|
+
content = content.gsub(/(<body[^>]*?)>/) do
|
112
|
+
body_tag = ::Regexp.last_match(1)
|
113
|
+
"#{body_tag} data-controller=\"turbo-turbo--modal\">"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Add flashes render after body tag if not present and not skipped
|
119
|
+
if !options[:skip_flash] && !(content.include?("render 'turbo_turbo/flashes'") || content.include?('render "turbo_turbo/flashes"'))
|
120
|
+
content = content.gsub(/(<body[^>]*>\s*)/) do
|
121
|
+
"#{::Regexp.last_match(1)} <%= render 'turbo_turbo/flashes' %>\n "
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Add modal background render if not present and not skipped
|
126
|
+
if !options[:skip_modal] && !(content.include?("render 'turbo_turbo/modal_background'") || content.include?('render "turbo_turbo/modal_background"'))
|
127
|
+
content = if content.include?("render 'turbo_turbo/flashes'") || content.include?('render "turbo_turbo/flashes"')
|
128
|
+
# Insert after flashes
|
129
|
+
content.gsub(%r{(.*render ['"]turbo_turbo/flashes['"].*\n\s*)}) do
|
130
|
+
"#{::Regexp.last_match(1)} <%= render 'turbo_turbo/modal_background' %>\n "
|
131
|
+
end
|
132
|
+
else
|
133
|
+
# Insert after body tag
|
134
|
+
content.gsub(/(<body[^>]*>\s*)/) do
|
135
|
+
"#{::Regexp.last_match(1)} <%= render 'turbo_turbo/modal_background' %>\n "
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add ModalComponent before closing body tag if not present and not skipped
|
141
|
+
if !options[:skip_modal] && !content.include?("TurboTurbo::ModalComponent")
|
142
|
+
content = content.gsub(%r{(\s*)</body>}) do
|
143
|
+
"#{::Regexp.last_match(1)} <%= render TurboTurbo::ModalComponent.new %>\n#{::Regexp.last_match(1)}</body>"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
content
|
148
|
+
end
|
149
|
+
|
150
|
+
def modify_slim_layout(content)
|
151
|
+
# Add turbo-turbo--modal to data-controller if not present and not skipped
|
152
|
+
unless options[:skip_modal]
|
153
|
+
if content.match?(/data-controller\s*=\s*["'][^"']*["']/)
|
154
|
+
# data-controller exists, check if turbo-turbo--modal is present
|
155
|
+
unless content.match?(/data-controller\s*=\s*["'][^"']*\bturbo-turbo--modal\b[^"']*["']/)
|
156
|
+
content = content.gsub(/(data-controller\s*=\s*["'])([^"']*)(['"])/) do
|
157
|
+
prefix = ::Regexp.last_match(1)
|
158
|
+
controller_list = ::Regexp.last_match(2)
|
159
|
+
suffix = ::Regexp.last_match(3)
|
160
|
+
controllers = controller_list.strip.split(/\s+/)
|
161
|
+
controllers << "turbo-turbo--modal" unless controllers.include?("turbo-turbo--modal")
|
162
|
+
"#{prefix}#{controllers.join(' ')}#{suffix}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
else
|
166
|
+
# Add data-controller with turbo-turbo--modal to body tag
|
167
|
+
content = content.gsub(/(^ *body(?:\.[a-zA-Z0-9_-]+)*)\s*$/m) do
|
168
|
+
body_line = ::Regexp.last_match(1)
|
169
|
+
"#{body_line} data-controller=\"turbo-turbo--modal\""
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Add flashes render after body tag if not present and not skipped
|
175
|
+
if !options[:skip_flash] && !(content.match?(%r{render\s+["']turbo_turbo/flashes["']}) || content.match?(%r{render\s+"turbo_turbo/flashes"}))
|
176
|
+
content = content.gsub(/(^ *body(?:\.[a-zA-Z0-9_-]+)*.*\n)/m) do
|
177
|
+
"#{::Regexp.last_match(1)} = render \"turbo_turbo/flashes\"\n"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Add modal background render if not present and not skipped
|
182
|
+
if !options[:skip_modal] && !(content.match?(%r{render\s+["']turbo_turbo/modal_background["']}) || content.match?(%r{render\s+"turbo_turbo/modal_background"}))
|
183
|
+
content = if content.match?(%r{render\s+["']turbo_turbo/flashes["']})
|
184
|
+
# Insert after flashes
|
185
|
+
content.gsub(%r{(.*render\s+["']turbo_turbo/flashes["'].*\n)}) do
|
186
|
+
"#{::Regexp.last_match(1)} = render \"turbo_turbo/modal_background\"\n"
|
187
|
+
end
|
188
|
+
else
|
189
|
+
# Insert after body tag
|
190
|
+
content.gsub(/(^ *body(?:\.[a-zA-Z0-9_-]+)*.*\n)/m) do
|
191
|
+
"#{::Regexp.last_match(1)} = render \"turbo_turbo/modal_background\"\n"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Add TurboTurbo::ModalComponent at end of body if not present and not skipped
|
197
|
+
if !options[:skip_modal] && !content.include?("TurboTurbo::ModalComponent")
|
198
|
+
# Find the last line with content before implicit body closing
|
199
|
+
lines = content.split("\n")
|
200
|
+
body_found = false
|
201
|
+
insert_index = -1
|
202
|
+
|
203
|
+
lines.each_with_index do |line, index|
|
204
|
+
if line.match?(/^\s*body/)
|
205
|
+
body_found = true
|
206
|
+
elsif body_found && line.strip.length.positive? && !line.match?(%r{^\s*/}) # not a comment
|
207
|
+
insert_index = index
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if insert_index >= 0
|
212
|
+
lines.insert(insert_index + 1, " = render TurboTurbo::ModalComponent.new")
|
213
|
+
content = lines.join("\n")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
content
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TurboTurbo routes for modal-based CRUD operations
|
4
|
+
# Add `draw(:turbo_turbo_routes)` to your main routes.rb file to include these routes
|
5
|
+
|
6
|
+
namespace :turbo_turbo do
|
7
|
+
# Admin namespace for administrative modal operations
|
8
|
+
namespace :admin do
|
9
|
+
# Add your admin resources here
|
10
|
+
# Example: resources :pathways, only: [:new, :create, :show, :edit, :update, :destroy]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Provider namespace for provider modal operations
|
14
|
+
namespace :provider do
|
15
|
+
# Add your provider resources here
|
16
|
+
# Example: resources :pathways, only: [:new, :create, :show, :edit, :update, :destroy]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Add other namespaces as needed
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="rounded-md bg-red-50 p-4 mb-4">
|
2
|
+
<div class="flex">
|
3
|
+
<div class="flex-shrink-0">
|
4
|
+
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
5
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
|
6
|
+
</svg>
|
7
|
+
</div>
|
8
|
+
<div class="ml-3">
|
9
|
+
<h3 class="text-sm font-medium text-red-800">There were errors with your submission</h3>
|
10
|
+
<div class="mt-2 text-sm text-red-700">
|
11
|
+
<%= message.html_safe %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<div id="flashes_turbo_turbo">
|
2
|
+
<% flash.each do |key, value| %>
|
3
|
+
<% key = convert_flash_keys(key) %>
|
4
|
+
<% next unless ApplicationController._flash_types.include?(key.to_sym) %>
|
5
|
+
<% value = { message: value } if value.is_a?(String) %>
|
6
|
+
<%= render("TurboTurbo::Alerts::#{key.titleize}Component".constantize.new(value)) %>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="turbo-turbo--flash"
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
setTimeout(() => this.fadeOut(this.element), (5000))
|
7
|
+
}
|
8
|
+
|
9
|
+
fadeOut(element){
|
10
|
+
var intervalID = setInterval(() => {
|
11
|
+
if (!element.style.opacity) {
|
12
|
+
element.style.opacity = 1;
|
13
|
+
}
|
14
|
+
|
15
|
+
if (element.style.opacity > 0) {
|
16
|
+
element.style.opacity -= 0.05;
|
17
|
+
} else {
|
18
|
+
clearInterval(intervalID);
|
19
|
+
element.classList.add("hidden")
|
20
|
+
}
|
21
|
+
}, 75);
|
22
|
+
}
|
23
|
+
|
24
|
+
dismiss(){
|
25
|
+
event.preventDefault();
|
26
|
+
this.element.classList.add("hidden");
|
27
|
+
}
|
28
|
+
}
|