infotable 0.1.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/MIT-LICENSE +20 -0
- data/README.md +1043 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/infotable/controllers/drawer_controller.js +1412 -0
- data/app/assets/javascripts/infotable/controllers/table_controller.js +921 -0
- data/app/assets/javascripts/infotable/index.js +14 -0
- data/app/controllers/infotable/application_controller.rb +4 -0
- data/app/controllers/infotable/tables_controller.rb +299 -0
- data/app/helpers/infotable/application_helper.rb +5 -0
- data/app/helpers/infotable/tables_helper.rb +354 -0
- data/app/jobs/infotable/application_job.rb +4 -0
- data/app/mailers/infotable/application_mailer.rb +6 -0
- data/app/models/infotable/application_record.rb +5 -0
- data/app/views/infotable/tables/_column_visibility.html.erb +100 -0
- data/app/views/infotable/tables/_filter_drawer.html.erb +65 -0
- data/app/views/infotable/tables/_next_page_frame.html.erb +39 -0
- data/app/views/infotable/tables/_pagination.html.erb +101 -0
- data/app/views/infotable/tables/_side_panel.html.erb +63 -0
- data/app/views/infotable/tables/_table.html.erb +135 -0
- data/app/views/infotable/tables/_table_rows.html.erb +38 -0
- data/app/views/infotable/tables/filters/_boolean.html.erb +22 -0
- data/app/views/infotable/tables/filters/_date.html.erb +30 -0
- data/app/views/infotable/tables/filters/_number.html.erb +32 -0
- data/app/views/infotable/tables/filters/_select.html.erb +68 -0
- data/app/views/infotable/tables/filters/_text.html.erb +38 -0
- data/app/views/infotable/tables/show.html.erb +113 -0
- data/app/views/layouts/infotable/application.html.erb +15 -0
- data/config/importmap.rb +6 -0
- data/config/locales/en.yml +88 -0
- data/config/locales/pl.yml +88 -0
- data/config/routes.rb +6 -0
- data/lib/generators/infotable/install/install_generator.rb +565 -0
- data/lib/generators/infotable/install/templates/TAILWIND_CONFIG.md +131 -0
- data/lib/generators/infotable/install/templates/initializer.rb +26 -0
- data/lib/generators/infotable/install/templates/tailwind.config.js +284 -0
- data/lib/generators/infotable/table/table_generator.rb +95 -0
- data/lib/generators/infotable/table/templates/table.rb.tt +26 -0
- data/lib/infotable/base.rb +463 -0
- data/lib/infotable/batch_actions.rb +34 -0
- data/lib/infotable/configuration.rb +53 -0
- data/lib/infotable/engine.rb +51 -0
- data/lib/infotable/filter_processor.rb +326 -0
- data/lib/infotable/formatters/action_formatter.rb +141 -0
- data/lib/infotable/formatters/avatar_formatter.rb +49 -0
- data/lib/infotable/formatters/badge_formatter.rb +80 -0
- data/lib/infotable/formatters/base_formatter.rb +47 -0
- data/lib/infotable/formatters/boolean_formatter.rb +38 -0
- data/lib/infotable/formatters/currency_formatter.rb +22 -0
- data/lib/infotable/formatters/date_formatter.rb +58 -0
- data/lib/infotable/formatters/link_formatter.rb +48 -0
- data/lib/infotable/formatters/percent_formatter.rb +27 -0
- data/lib/infotable/search_processor.rb +42 -0
- data/lib/infotable/table_resolver.rb +45 -0
- data/lib/infotable/version.rb +3 -0
- data/lib/infotable.rb +54 -0
- data/lib/tasks/infotable.rake +55 -0
- metadata +216 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module Infotable
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Install InfoTable into your application"
|
|
9
|
+
|
|
10
|
+
def check_tailwindcss_rails_gem
|
|
11
|
+
begin
|
|
12
|
+
gem_spec = Bundler.load.specs.find { |s| s.name == "tailwindcss-rails" }
|
|
13
|
+
|
|
14
|
+
if gem_spec.nil?
|
|
15
|
+
say "\n" + "=" * 60, :red
|
|
16
|
+
say "❌ ERROR: tailwindcss-rails gem not found", :red
|
|
17
|
+
say "=" * 60, :red
|
|
18
|
+
say "\nInfoTable requires the tailwindcss-rails gem to be installed.", :yellow
|
|
19
|
+
say "This gem provides Tailwind CSS v4 support for Rails applications.\n", :yellow
|
|
20
|
+
say "To install, add to your Gemfile:", :cyan
|
|
21
|
+
say " gem 'tailwindcss-rails'", :green
|
|
22
|
+
say "\nThen run:", :cyan
|
|
23
|
+
say " bundle install", :green
|
|
24
|
+
say " rails tailwindcss:install", :green
|
|
25
|
+
say "\nAfter installing tailwindcss-rails, run this installer again:", :cyan
|
|
26
|
+
say " rails generate infotable:install", :green
|
|
27
|
+
say "\n" + "=" * 60, :red
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
say "✅ Found tailwindcss-rails gem (version #{gem_spec.version})", :green
|
|
32
|
+
rescue => e
|
|
33
|
+
say "⚠️ Could not verify tailwindcss-rails gem: #{e.message}", :yellow
|
|
34
|
+
say " Continuing with installation...", :yellow
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def detect_js_bundler
|
|
39
|
+
@js_bundler = if File.exist?("config/importmap.rb")
|
|
40
|
+
:importmap
|
|
41
|
+
elsif File.exist?("esbuild.config.js") || package_json_has?("esbuild")
|
|
42
|
+
:esbuild
|
|
43
|
+
elsif File.exist?("vite.config.ts") || package_json_has?("vite")
|
|
44
|
+
:vite
|
|
45
|
+
elsif File.exist?("webpack.config.js") || package_json_has?("webpack")
|
|
46
|
+
:webpack
|
|
47
|
+
else
|
|
48
|
+
:unknown
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
say "Detected JavaScript bundler: #{@js_bundler}", :green
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def add_routes
|
|
55
|
+
route 'mount Infotable::Engine => "/infotable"'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_infotable_directory
|
|
59
|
+
empty_directory "app/infotable"
|
|
60
|
+
create_file "app/infotable/.keep"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def setup_javascript
|
|
64
|
+
case @js_bundler
|
|
65
|
+
when :importmap
|
|
66
|
+
setup_importmap
|
|
67
|
+
when :esbuild, :webpack, :vite
|
|
68
|
+
setup_bundler
|
|
69
|
+
else
|
|
70
|
+
say "⚠️ Could not detect JavaScript bundler. Please see manual setup instructions.", :yellow
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def setup_tailwind
|
|
75
|
+
# Check for Tailwind CSS file from tailwindcss-rails gem
|
|
76
|
+
if File.exist?("app/assets/stylesheets/application.tailwind.css") ||
|
|
77
|
+
File.exist?("app/assets/tailwind/application.css")
|
|
78
|
+
say "✅ Detected Tailwind CSS configuration", :green
|
|
79
|
+
append_tailwind_v4_sources
|
|
80
|
+
else
|
|
81
|
+
say "\n" + "=" * 60, :yellow
|
|
82
|
+
say "⚠️ Tailwind CSS file not found", :yellow
|
|
83
|
+
say "=" * 60, :yellow
|
|
84
|
+
say "\nInfoTable requires tailwindcss-rails to be installed and configured.", :yellow
|
|
85
|
+
say "\nPlease run:", :cyan
|
|
86
|
+
say " rails tailwindcss:install", :green
|
|
87
|
+
say "\nThis will create the required Tailwind CSS configuration.", :cyan
|
|
88
|
+
say "After that, run this installer again:", :cyan
|
|
89
|
+
say " rails generate infotable:install", :green
|
|
90
|
+
say "\n" + "=" * 60, :yellow
|
|
91
|
+
exit 1
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def create_initializer
|
|
96
|
+
template "initializer.rb", "config/initializers/infotable.rb"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def add_to_gitignore
|
|
100
|
+
gitignore_entry = "# InfoTable - Generated from ERB template, do not commit\napp/assets/stylesheets/infotable.css\napp/assets/tailwind/infotable.css\n"
|
|
101
|
+
|
|
102
|
+
if File.exist?(".gitignore")
|
|
103
|
+
content = File.read(".gitignore")
|
|
104
|
+
unless content.include?("infotable.css")
|
|
105
|
+
append_to_file ".gitignore", "\n#{gitignore_entry}"
|
|
106
|
+
say " ✅ Added generated infotable.css to .gitignore", :green
|
|
107
|
+
else
|
|
108
|
+
say " ℹ️ infotable.css already in .gitignore", :blue
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
create_file ".gitignore", gitignore_entry
|
|
112
|
+
say " ✅ Created .gitignore with infotable.css", :green
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
say " 📝 Commit infotable.css.erb (the template), not infotable.css (generated)", :cyan
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def show_post_install_message
|
|
119
|
+
say "\n" + "=" * 60, :green
|
|
120
|
+
say "✅ InfoTable installation complete!", :green
|
|
121
|
+
say "=" * 60, :green
|
|
122
|
+
say "\nNext steps:", :cyan
|
|
123
|
+
say "1. Rebuild your assets:", :yellow
|
|
124
|
+
|
|
125
|
+
if @js_bundler == :esbuild
|
|
126
|
+
say " npm run build", :green
|
|
127
|
+
elsif @js_bundler == :webpack
|
|
128
|
+
say " npm run build", :green
|
|
129
|
+
elsif @js_bundler == :vite
|
|
130
|
+
say " npm run build", :green
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
say " rails tailwindcss:build", :green
|
|
134
|
+
|
|
135
|
+
say "\n2. Verify Tailwind CSS is loaded in your layout:", :yellow
|
|
136
|
+
say " Ensure your application layout includes:", :cyan
|
|
137
|
+
say " <%= stylesheet_link_tag \"tailwind\", \"data-turbo-track\": \"reload\" %>", :green
|
|
138
|
+
|
|
139
|
+
say "\n3. Restart your Rails server", :yellow
|
|
140
|
+
say "4. Create your first table definition in app/infotable/", :yellow
|
|
141
|
+
say "5. Use <%= infotable :your_table_name %> in your views", :yellow
|
|
142
|
+
|
|
143
|
+
say "\n" + "=" * 60, :cyan
|
|
144
|
+
say "💡 Important Notes:", :cyan
|
|
145
|
+
say "=" * 60, :cyan
|
|
146
|
+
say "• InfoTable requires the tailwindcss-rails gem", :green
|
|
147
|
+
say "• infotable.css.erb is processed by Rake task before Tailwind builds", :green
|
|
148
|
+
say "• Gem paths resolve dynamically for each environment (dev/production)", :green
|
|
149
|
+
say "• Commit infotable.css.erb (template), gitignore infotable.css (generated)", :cyan
|
|
150
|
+
|
|
151
|
+
say "\n📖 Documentation: https://github.com/jcieslar/infotable", :cyan
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def setup_importmap
|
|
157
|
+
say "Configuring for importmap...", :green
|
|
158
|
+
# Importmap pins are handled by the engine's importmap.rb
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def setup_bundler
|
|
162
|
+
say "Configuring for #{@js_bundler}...", :green
|
|
163
|
+
|
|
164
|
+
# Find the infotable gem path
|
|
165
|
+
gem_path = find_gem_path
|
|
166
|
+
|
|
167
|
+
case @js_bundler
|
|
168
|
+
when :esbuild
|
|
169
|
+
setup_esbuild(gem_path)
|
|
170
|
+
when :webpack
|
|
171
|
+
setup_webpack(gem_path)
|
|
172
|
+
when :vite
|
|
173
|
+
setup_vite(gem_path)
|
|
174
|
+
else
|
|
175
|
+
say "⚠️ Unknown bundler. Please configure manually.", :yellow
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def setup_esbuild(gem_path)
|
|
180
|
+
# Check for required peer dependencies
|
|
181
|
+
ensure_npm_dependencies
|
|
182
|
+
|
|
183
|
+
# Add import for InfoTable controllers from asset paths
|
|
184
|
+
if File.exist?("app/javascript/controllers/index.js")
|
|
185
|
+
content = File.read("app/javascript/controllers/index.js")
|
|
186
|
+
|
|
187
|
+
# Check if InfoTable controllers are already registered
|
|
188
|
+
unless content.include?("infotable")
|
|
189
|
+
# Check if application is already imported
|
|
190
|
+
unless content.match?(/from\s+['"]\.\/application['"]/)
|
|
191
|
+
inject_into_file "app/javascript/controllers/index.js", after: /import.*\n/ do
|
|
192
|
+
"import { application } from \"./application\"\n"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
js_content = <<~JS
|
|
197
|
+
|
|
198
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
199
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
200
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
201
|
+
application.register("infotable--table", InfoTableTableController)
|
|
202
|
+
application.register("infotable--drawer", InfoTableDrawerController)
|
|
203
|
+
JS
|
|
204
|
+
|
|
205
|
+
append_to_file "app/javascript/controllers/index.js", js_content
|
|
206
|
+
say " ✅ Added InfoTable controllers to controllers/index.js", :green
|
|
207
|
+
else
|
|
208
|
+
say " ℹ️ InfoTable controllers already registered in controllers/index.js", :blue
|
|
209
|
+
end
|
|
210
|
+
elsif File.exist?("app/javascript/application.js")
|
|
211
|
+
content = File.read("app/javascript/application.js")
|
|
212
|
+
|
|
213
|
+
unless content.include?("infotable")
|
|
214
|
+
# Find the application instance in application.js
|
|
215
|
+
application_var = if content.include?("const application")
|
|
216
|
+
"application"
|
|
217
|
+
elsif content.include?("export { application }")
|
|
218
|
+
"application"
|
|
219
|
+
else
|
|
220
|
+
"Stimulus.Application.start()"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
js_content = <<~JS
|
|
224
|
+
|
|
225
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
226
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
227
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
228
|
+
#{application_var}.register("infotable--table", InfoTableTableController)
|
|
229
|
+
#{application_var}.register("infotable--drawer", InfoTableDrawerController)
|
|
230
|
+
JS
|
|
231
|
+
|
|
232
|
+
append_to_file "app/javascript/application.js", js_content
|
|
233
|
+
say " ✅ Added InfoTable controllers to application.js", :green
|
|
234
|
+
else
|
|
235
|
+
say " ℹ️ InfoTable controllers already registered in application.js", :blue
|
|
236
|
+
end
|
|
237
|
+
else
|
|
238
|
+
say " ⚠️ Could not find controllers/index.js or application.js", :yellow
|
|
239
|
+
say " Please manually import InfoTable controllers", :yellow
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
say "✅ esbuild configured successfully", :green
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def setup_webpack(gem_path)
|
|
246
|
+
# Check for required peer dependencies
|
|
247
|
+
ensure_npm_dependencies
|
|
248
|
+
|
|
249
|
+
# Add import for InfoTable controllers from asset paths
|
|
250
|
+
if File.exist?("app/javascript/controllers/index.js")
|
|
251
|
+
content = File.read("app/javascript/controllers/index.js")
|
|
252
|
+
|
|
253
|
+
unless content.include?("infotable")
|
|
254
|
+
# Check if application is already imported
|
|
255
|
+
unless content.match?(/from\s+['"]\.\/application['"]/)
|
|
256
|
+
inject_into_file "app/javascript/controllers/index.js", after: /import.*\n/ do
|
|
257
|
+
"import { application } from \"./application\"\n"
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
js_content = <<~JS
|
|
262
|
+
|
|
263
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
264
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
265
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
266
|
+
application.register("infotable--table", InfoTableTableController)
|
|
267
|
+
application.register("infotable--drawer", InfoTableDrawerController)
|
|
268
|
+
JS
|
|
269
|
+
|
|
270
|
+
append_to_file "app/javascript/controllers/index.js", js_content
|
|
271
|
+
say " ✅ Added InfoTable controllers to controllers/index.js", :green
|
|
272
|
+
else
|
|
273
|
+
say " ℹ️ InfoTable controllers already registered", :blue
|
|
274
|
+
end
|
|
275
|
+
elsif File.exist?("app/javascript/application.js")
|
|
276
|
+
content = File.read("app/javascript/application.js")
|
|
277
|
+
|
|
278
|
+
unless content.include?("infotable")
|
|
279
|
+
# Find the application instance in application.js
|
|
280
|
+
application_var = if content.include?("const application")
|
|
281
|
+
"application"
|
|
282
|
+
elsif content.include?("export { application }")
|
|
283
|
+
"application"
|
|
284
|
+
else
|
|
285
|
+
"Stimulus.Application.start()"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
js_content = <<~JS
|
|
289
|
+
|
|
290
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
291
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
292
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
293
|
+
#{application_var}.register("infotable--table", InfoTableTableController)
|
|
294
|
+
#{application_var}.register("infotable--drawer", InfoTableDrawerController)
|
|
295
|
+
JS
|
|
296
|
+
|
|
297
|
+
append_to_file "app/javascript/application.js", js_content
|
|
298
|
+
say " ✅ Added InfoTable controllers to application.js", :green
|
|
299
|
+
else
|
|
300
|
+
say " ℹ️ InfoTable controllers already registered", :blue
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
say "✅ webpack configured successfully", :green
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def setup_vite(gem_path)
|
|
308
|
+
# Check for required peer dependencies
|
|
309
|
+
ensure_npm_dependencies
|
|
310
|
+
|
|
311
|
+
# Add import for InfoTable controllers from asset paths
|
|
312
|
+
if File.exist?("app/javascript/controllers/index.js")
|
|
313
|
+
content = File.read("app/javascript/controllers/index.js")
|
|
314
|
+
|
|
315
|
+
unless content.include?("infotable")
|
|
316
|
+
# Check if application is already imported
|
|
317
|
+
unless content.match?(/from\s+['"]\.\/application['"]/)
|
|
318
|
+
inject_into_file "app/javascript/controllers/index.js", after: /import.*\n/ do
|
|
319
|
+
"import { application } from \"./application\"\n"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
js_content = <<~JS
|
|
324
|
+
|
|
325
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
326
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
327
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
328
|
+
application.register("infotable--table", InfoTableTableController)
|
|
329
|
+
application.register("infotable--drawer", InfoTableDrawerController)
|
|
330
|
+
JS
|
|
331
|
+
|
|
332
|
+
append_to_file "app/javascript/controllers/index.js", js_content
|
|
333
|
+
say " ✅ Added InfoTable controllers to controllers/index.js", :green
|
|
334
|
+
else
|
|
335
|
+
say " ℹ️ InfoTable controllers already registered", :blue
|
|
336
|
+
end
|
|
337
|
+
elsif File.exist?("app/javascript/application.js")
|
|
338
|
+
content = File.read("app/javascript/application.js")
|
|
339
|
+
|
|
340
|
+
unless content.include?("infotable")
|
|
341
|
+
# Find the application instance in application.js
|
|
342
|
+
application_var = if content.include?("const application")
|
|
343
|
+
"application"
|
|
344
|
+
elsif content.include?("export { application }")
|
|
345
|
+
"application"
|
|
346
|
+
else
|
|
347
|
+
"Stimulus.Application.start()"
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
js_content = <<~JS
|
|
351
|
+
|
|
352
|
+
// InfoTable Stimulus controllers (resolved from Rails asset paths)
|
|
353
|
+
import InfoTableTableController from "infotable/controllers/table_controller"
|
|
354
|
+
import InfoTableDrawerController from "infotable/controllers/drawer_controller"
|
|
355
|
+
#{application_var}.register("infotable--table", InfoTableTableController)
|
|
356
|
+
#{application_var}.register("infotable--drawer", InfoTableDrawerController)
|
|
357
|
+
JS
|
|
358
|
+
|
|
359
|
+
append_to_file "app/javascript/application.js", js_content
|
|
360
|
+
say " ✅ Added InfoTable controllers to application.js", :green
|
|
361
|
+
else
|
|
362
|
+
say " ℹ️ InfoTable controllers already registered", :blue
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
say "✅ vite configured successfully", :green
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def create_vite_config(gem_path)
|
|
370
|
+
return if File.exist?("vite.config.ts") || File.exist?("vite.config.js")
|
|
371
|
+
|
|
372
|
+
vite_config = <<~JS
|
|
373
|
+
import { defineConfig } from 'vite'
|
|
374
|
+
import RubyPlugin from 'vite-plugin-ruby'
|
|
375
|
+
|
|
376
|
+
export default defineConfig({
|
|
377
|
+
plugins: [RubyPlugin()],
|
|
378
|
+
resolve: {
|
|
379
|
+
alias: {
|
|
380
|
+
'infotable': '#{gem_path}/app/assets/javascripts/infotable'
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
JS
|
|
385
|
+
|
|
386
|
+
create_file "vite.config.ts", vite_config
|
|
387
|
+
say " Created vite.config.ts with infotable alias", :green
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def should_create_vite_config?
|
|
391
|
+
!File.exist?("vite.config.ts") && !File.exist?("vite.config.js")
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def ensure_npm_dependencies
|
|
395
|
+
# Only check for bundlers that require npm packages
|
|
396
|
+
return unless [ :esbuild, :webpack, :vite ].include?(@js_bundler)
|
|
397
|
+
|
|
398
|
+
unless File.exist?("package.json")
|
|
399
|
+
say " ⚠️ No package.json found", :yellow
|
|
400
|
+
say " 📝 You'll need to install npm dependencies manually", :cyan
|
|
401
|
+
return
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
package_json = JSON.parse(File.read("package.json"))
|
|
405
|
+
package_json["dependencies"] ||= {}
|
|
406
|
+
package_json["devDependencies"] ||= {}
|
|
407
|
+
|
|
408
|
+
missing_deps = []
|
|
409
|
+
missing_tailwind_deps = []
|
|
410
|
+
|
|
411
|
+
# Check for required peer dependencies
|
|
412
|
+
unless package_json["dependencies"]["@hotwired/stimulus"] || package_json["devDependencies"]["@hotwired/stimulus"]
|
|
413
|
+
missing_deps << "@hotwired/stimulus"
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
unless package_json["dependencies"]["sortablejs"] || package_json["devDependencies"]["sortablejs"]
|
|
417
|
+
missing_deps << "sortablejs"
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Check for Tailwind plugin (recommended, not required)
|
|
421
|
+
unless package_json["dependencies"]["@tailwindcss/forms"] || package_json["devDependencies"]["@tailwindcss/forms"]
|
|
422
|
+
missing_tailwind_deps << "@tailwindcss/forms"
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
if missing_deps.any?
|
|
426
|
+
say "\n" + "=" * 60, :red
|
|
427
|
+
say "❌ ERROR: Missing required npm dependencies", :red
|
|
428
|
+
say "=" * 60, :red
|
|
429
|
+
say "\nInfoTable requires the following npm packages:", :yellow
|
|
430
|
+
missing_deps.each do |dep|
|
|
431
|
+
say " • #{dep}", :yellow
|
|
432
|
+
end
|
|
433
|
+
say "\nThese are required for esbuild/webpack/vite to bundle the controllers.", :cyan
|
|
434
|
+
say "\nInstall them by running:", :cyan
|
|
435
|
+
|
|
436
|
+
package_manager = if File.exist?("yarn.lock")
|
|
437
|
+
"yarn add @hotwired/stimulus sortablejs"
|
|
438
|
+
elsif File.exist?("pnpm-lock.yaml")
|
|
439
|
+
"pnpm add @hotwired/stimulus sortablejs"
|
|
440
|
+
else
|
|
441
|
+
"npm install @hotwired/stimulus sortablejs"
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
say " #{package_manager}", :green
|
|
445
|
+
say "\nAfter installing, restart your dev server.", :cyan
|
|
446
|
+
say "=" * 60, :red
|
|
447
|
+
exit 1
|
|
448
|
+
else
|
|
449
|
+
say " ✅ All required npm dependencies are installed", :green
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
if missing_tailwind_deps.any?
|
|
453
|
+
say " ℹ️ Recommended Tailwind plugins:", :cyan
|
|
454
|
+
missing_tailwind_deps.each do |dep|
|
|
455
|
+
say " - #{dep}", :cyan
|
|
456
|
+
end
|
|
457
|
+
say " Install with: npm install -D @tailwindcss/forms", :cyan
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
rescue JSON::ParserError => e
|
|
461
|
+
say "⚠️ Could not parse package.json: #{e.message}", :yellow
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def add_controller_registration
|
|
465
|
+
# This method is no longer needed with npm package approach
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def find_gem_path
|
|
469
|
+
# Use Bundler to find the gem path - works with path:, git:, and rubygems installs
|
|
470
|
+
if defined?(Bundler)
|
|
471
|
+
begin
|
|
472
|
+
spec = Bundler.load.specs.find { |s| s.name == "infotable" }
|
|
473
|
+
return spec.full_gem_path if spec
|
|
474
|
+
rescue => e
|
|
475
|
+
say " ⚠️ Could not find gem via Bundler: #{e.message}", :yellow
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Fallback to Gem::Specification
|
|
480
|
+
begin
|
|
481
|
+
spec = Gem::Specification.find_by_name("infotable")
|
|
482
|
+
spec.gem_dir
|
|
483
|
+
rescue Gem::MissingSpecError
|
|
484
|
+
# Last resort: use relative path from generator location
|
|
485
|
+
File.expand_path("../../..", __dir__)
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def append_tailwind_v4_sources
|
|
490
|
+
tailwind_file = if File.exist?("app/assets/stylesheets/application.tailwind.css")
|
|
491
|
+
"app/assets/stylesheets/application.tailwind.css"
|
|
492
|
+
elsif File.exist?("app/assets/tailwind/application.css")
|
|
493
|
+
"app/assets/tailwind/application.css"
|
|
494
|
+
else
|
|
495
|
+
say " ⚠️ Tailwind CSS file not found", :yellow
|
|
496
|
+
return
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Create separate infotable.css file with resolved gem paths
|
|
500
|
+
create_infotable_css_file
|
|
501
|
+
|
|
502
|
+
# Tailwind only scans the gem's views if the main CSS imports the generated file
|
|
503
|
+
content = File.read(tailwind_file)
|
|
504
|
+
if content.match?(/@import\s+["']\.?\/?infotable["']/)
|
|
505
|
+
say " ℹ️ InfoTable import already exists in #{File.basename(tailwind_file)}", :blue
|
|
506
|
+
else
|
|
507
|
+
if content.include?("@import \"tailwindcss\"")
|
|
508
|
+
inject_into_file tailwind_file, after: /@import\s+["']tailwindcss["'];?\s*\n/ do
|
|
509
|
+
"\n/* InfoTable - Requires tailwindcss-rails gem */\n@import './infotable';\n"
|
|
510
|
+
end
|
|
511
|
+
else
|
|
512
|
+
prepend_to_file tailwind_file, "/* InfoTable - Requires tailwindcss-rails gem */\n@import './infotable';\n\n"
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
say " ✅ Added @import './infotable' to #{File.basename(tailwind_file)}", :green
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
say " 📝 Run: rails tailwindcss:build", :cyan
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def create_infotable_css_file
|
|
522
|
+
# Determine the stylesheets directory
|
|
523
|
+
stylesheets_dir = if File.exist?("app/assets/stylesheets")
|
|
524
|
+
"app/assets/stylesheets"
|
|
525
|
+
elsif File.exist?("app/assets/tailwind")
|
|
526
|
+
"app/assets/tailwind"
|
|
527
|
+
else
|
|
528
|
+
"app/assets/stylesheets"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
infotable_css_path = File.join(stylesheets_dir, "infotable.css.erb")
|
|
532
|
+
|
|
533
|
+
# Create ERB file that dynamically resolves gem paths
|
|
534
|
+
css_content = <<~CSS
|
|
535
|
+
/* InfoTable Tailwind Sources - ERB Template */
|
|
536
|
+
/* Requires: tailwindcss-rails gem (https://github.com/rails/tailwindcss-rails) */
|
|
537
|
+
/* Paths are resolved dynamically via ERB from Infotable module */
|
|
538
|
+
|
|
539
|
+
/* Local table definitions in your Rails app */
|
|
540
|
+
@source "../../app/infotable";
|
|
541
|
+
|
|
542
|
+
/* InfoTable gem paths (resolved via ERB at build time) */
|
|
543
|
+
@source "<%= Infotable.views_path %>";
|
|
544
|
+
@source "<%= Infotable.formatters_path %>";
|
|
545
|
+
CSS
|
|
546
|
+
|
|
547
|
+
create_file infotable_css_path, css_content
|
|
548
|
+
|
|
549
|
+
say " ✅ Created #{infotable_css_path} with ERB templates", :green
|
|
550
|
+
say " 📝 Gem paths resolve dynamically via Infotable module", :cyan
|
|
551
|
+
say " 📝 Works in all environments automatically", :green
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def package_json_has?(package)
|
|
555
|
+
return false unless File.exist?("package.json")
|
|
556
|
+
|
|
557
|
+
package_json = JSON.parse(File.read("package.json"))
|
|
558
|
+
(package_json["dependencies"]&.key?(package) ||
|
|
559
|
+
package_json["devDependencies"]&.key?(package))
|
|
560
|
+
rescue JSON::ParserError
|
|
561
|
+
false
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Tailwind Configuration for InfoTable
|
|
2
|
+
|
|
3
|
+
InfoTable supports both Tailwind CSS v4 (recommended) and v3.
|
|
4
|
+
|
|
5
|
+
## Tailwind v4 (CSS-based Config) - RECOMMENDED
|
|
6
|
+
|
|
7
|
+
Tailwind v4 uses CSS-based configuration with `@source` directives. The installer creates a separate CSS file for InfoTable.
|
|
8
|
+
|
|
9
|
+
### What the Installer Does
|
|
10
|
+
|
|
11
|
+
When you run `rails generate infotable:install`, it:
|
|
12
|
+
|
|
13
|
+
1. **Creates `app/assets/stylesheets/infotable.css`** with ENV variable references:
|
|
14
|
+
```css
|
|
15
|
+
/* InfoTable Tailwind Sources */
|
|
16
|
+
|
|
17
|
+
/* Local table definitions in your Rails app */
|
|
18
|
+
@source "../../app/infotable";
|
|
19
|
+
|
|
20
|
+
/* InfoTable gem paths (resolved dynamically via ENV variables) */
|
|
21
|
+
@source "$INFOTABLE_VIEWS_PATH";
|
|
22
|
+
@source "$INFOTABLE_FORMATTERS_PATH";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. **Adds import to `application.tailwind.css`**:
|
|
26
|
+
```css
|
|
27
|
+
@import "tailwindcss";
|
|
28
|
+
|
|
29
|
+
/* InfoTable gem styles */
|
|
30
|
+
@import './infotable';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Why This Approach?
|
|
34
|
+
|
|
35
|
+
- **Always correct**: Rails engine knows the exact gem location
|
|
36
|
+
- **Environment-agnostic**: Works in dev, staging, production, Docker, CI
|
|
37
|
+
- **Safe to commit**: No environment-specific paths
|
|
38
|
+
- **Zero maintenance**: Install once, works everywhere
|
|
39
|
+
- **Simple**: No glob patterns or complex path logic
|
|
40
|
+
|
|
41
|
+
### ENV Variable Strategy
|
|
42
|
+
|
|
43
|
+
The InfoTable engine sets ENV variables at Rails boot time:
|
|
44
|
+
```ruby
|
|
45
|
+
# lib/infotable/engine.rb
|
|
46
|
+
initializer "infotable.tailwind" do
|
|
47
|
+
ENV["INFOTABLE_VIEWS_PATH"] = root.join("app/views/infotable").to_s
|
|
48
|
+
ENV["INFOTABLE_FORMATTERS_PATH"] = root.join("lib/infotable/formatters").to_s
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Tailwind v4 expands these ENV variables when building:
|
|
53
|
+
```css
|
|
54
|
+
@source "$INFOTABLE_VIEWS_PATH";
|
|
55
|
+
```
|
|
56
|
+
→ Expands to the actual gem path at build time
|
|
57
|
+
|
|
58
|
+
### Works Everywhere Automatically
|
|
59
|
+
|
|
60
|
+
The same CSS file works in all environments:
|
|
61
|
+
- **Development**: ENV variables point to your local gem location
|
|
62
|
+
- **Production**: ENV variables point to production gem location
|
|
63
|
+
- **CI/CD**: ENV variables point to CI gem location
|
|
64
|
+
- **Docker**: ENV variables point to container gem location
|
|
65
|
+
|
|
66
|
+
Rails `Infotable::Engine.root` always returns the correct path!
|
|
67
|
+
|
|
68
|
+
You can safely commit `infotable.css` to version control!
|
|
69
|
+
|
|
70
|
+
## Tailwind v3 (JS-based Config) - FALLBACK
|
|
71
|
+
|
|
72
|
+
If using Tailwind v3, the installer adds dynamic path resolution to your `tailwind.config.js`:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
const { execSync } = require('child_process');
|
|
76
|
+
const path = require('path');
|
|
77
|
+
|
|
78
|
+
// Helper to resolve gem path dynamically
|
|
79
|
+
function resolveGemPath(gemName) {
|
|
80
|
+
try {
|
|
81
|
+
const bundlerCmd = `bundle show ${gemName}`;
|
|
82
|
+
const gemPath = execSync(bundlerCmd, { encoding: 'utf-8' }).trim();
|
|
83
|
+
return gemPath;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn(`Warning: Could not resolve ${gemName} gem path:`, error.message);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const infotableGemPath = resolveGemPath('infotable');
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
content: [
|
|
94
|
+
'./app/infotable/**/*.rb',
|
|
95
|
+
...(infotableGemPath ? [
|
|
96
|
+
path.join(infotableGemPath, 'app/views/infotable/**/*.{erb,rb}'),
|
|
97
|
+
path.join(infotableGemPath, 'app/assets/javascripts/infotable/**/*.js'),
|
|
98
|
+
path.join(infotableGemPath, 'lib/infotable/formatters/**/*.rb'),
|
|
99
|
+
path.join(infotableGemPath, 'app/components/**/*.{erb,rb}'),
|
|
100
|
+
] : []),
|
|
101
|
+
],
|
|
102
|
+
// ... rest of config
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This approach dynamically resolves the gem path at **build time** (every time Tailwind builds).
|
|
107
|
+
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
### Styles Not Appearing
|
|
111
|
+
|
|
112
|
+
1. Check your Tailwind CSS file includes InfoTable @source directives
|
|
113
|
+
2. Verify gem path is correct: `bundle show infotable`
|
|
114
|
+
3. Update paths if needed: `rake infotable:update_tailwind_paths`
|
|
115
|
+
4. Rebuild: `rails tailwindcss:build`
|
|
116
|
+
|
|
117
|
+
### After Changing Gem Installation Method
|
|
118
|
+
|
|
119
|
+
If you switch from `path:` to `git:` or vice versa:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
rake infotable:update_tailwind_paths
|
|
123
|
+
rails tailwindcss:build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Why Different Approaches?
|
|
127
|
+
|
|
128
|
+
- **Tailwind v4**: @source doesn't support variables, so paths must be static and resolved at install time
|
|
129
|
+
- **Tailwind v3**: JavaScript config supports dynamic resolution via `execSync`, so paths are resolved at build time
|
|
130
|
+
|
|
131
|
+
Both approaches work reliably, but v4 is recommended for better Rails integration and performance.
|