fluxbit_view_components 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16de56192c4f954e22b5391bbd8e94970a8345ede43ffaf8866050222a8d486a
4
- data.tar.gz: fb3a6c03297fc0836ee3c61005e530cc32640e0c97dbc872945b661e6e2b95c6
3
+ metadata.gz: fbaf175424393a228d6fc82018fcd81b93dc388df861dd8afc1fb4ce0ab901b1
4
+ data.tar.gz: '0728a9d9f386fcb06bf8e21898e39c0cf58a9b2f44763acfb6fe591d251f2600'
5
5
  SHA512:
6
- metadata.gz: 9127a488dc9bec5cc886886855edb7649e62a9ce36406e0c9aef4f956eb392fd9f21f42473459885eeb9758f19528c3185bd8729398852d91e17a27168a6cb40
7
- data.tar.gz: 3bced6391398780c7877563c35f406ede7c4dc838c2ac9e410c99db815c1c58686d79aa034eff979c561010f191701329a7daa9c857ed06b521b6441ff9c502d
6
+ metadata.gz: 618572080999a095ed82ed77875fae33507d208c5c0abbca39bd410742502e651215f3f7c5cedab195baf0d2a486aaa5ce45fcc4910646ab7806f33454c6d98f
7
+ data.tar.gz: e1e05c2e0445d8c9f92bae5f3580e3f08f235c65b0bab273538ca45acd3f07df2e2e35429934178883682c89ace072816010c79ae2ac435b6b9892894356a420
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Fluxbit ViewComponents
2
2
 
3
+ ![Gem Downloads (for latest version)](https://img.shields.io/gem/dtv/fluxbit_view_components) ![GitHub License](https://img.shields.io/github/license/arthurmolina/fluxbit_view_components) ![GitHub last commit](https://img.shields.io/github/last-commit/arthurmolina/fluxbit_view_components) ![Gem Downloads (for latest version)](https://img.shields.io/gem/dtv/fluxbit_view_components) ![Gem Total Downloads](https://img.shields.io/gem/dt/fluxbit_view_components) ![GitHub forks](https://img.shields.io/github/forks/arthurmolina/fluxbit_view_components)
4
+
5
+
6
+
3
7
  Fluxbit ViewComponents is an implementation of the Fluxbit Design System using [ViewComponent](https://github.com/github/view_component).
4
8
 
5
9
  <div style="text-align: center;">
@@ -8,7 +12,9 @@ Fluxbit ViewComponents is an implementation of the Fluxbit Design System using [
8
12
 
9
13
  ## Preview
10
14
 
11
- I don't have a site to host the lookbook. Sorry.
15
+ We have a Lookbook app online to show the documentation and all the Components available in action!
16
+
17
+ Just [Click here!](https://fluxbit.artz.to)
12
18
 
13
19
  ## Usage
14
20
 
@@ -24,9 +24,10 @@ class Fluxbit::Form::Component < Fluxbit::Component
24
24
  end
25
25
 
26
26
  def define_helper_popover(helper_popover, object, field)
27
- return helper_popover if helper_popover != false && !helper_popover.nil?
27
+ return helper_popover if (helper_popover != false && !helper_popover.nil?) || object.nil?
28
28
 
29
- I18n.t(field, scope: [ :activerecord, :helper_popover, object.class.name.underscore.to_sym ], default: nil)
29
+ object_name = object.class.name.underscore.to_sym
30
+ I18n.t(field, scope: [ :activerecord, :helper_popover, object_name ], default: nil)
30
31
  end
31
32
 
32
33
  def label_value(label, object, field, id)
@@ -79,6 +79,7 @@ class Fluxbit::Form::FormBuilderComponent < Fluxbit::Component
79
79
  else
80
80
  ''
81
81
  end}#{element_type(kwargs[:type])}Component".constantize
82
+
82
83
  unless kwargs[:with_content]
83
84
  return content_tag(:div, render(component_klass.new(**kwargs), &block), class: outer_div)
84
85
  end
@@ -104,14 +105,11 @@ class Fluxbit::Form::FormBuilderComponent < Fluxbit::Component
104
105
  def generate_elements
105
106
  return elements if elements?
106
107
 
107
- safe_join(*@elements.map { |element| choose_element(element.merge({ form: @form }), nil) })
108
- end
109
-
110
- def generate_div
111
- safe_join errors?, content_tag(:div, generate_elements, @props)
108
+ safe_join(@elements.map { |element| choose_element(element.merge({ form: @form }), nil) })
112
109
  end
113
110
 
114
111
  def call
115
- generate_div
112
+ elements_rendered = elements? ? elements : safe_join(@elements.map { |element| choose_element(element.merge({ form: @form }), nil) })
113
+ safe_join [ errors?, content_tag(:div, elements_rendered, @props) ]
116
114
  end
117
115
  end
@@ -72,6 +72,6 @@ class Fluxbit::Form::SelectFreeInputComponent < Fluxbit::Form::Component
72
72
  end
73
73
 
74
74
  def value
75
- @kwargs[:value] || @kwargs[:form].object[@kwargs[:field]]
75
+ @kwargs[:value] || @kwargs[:form]&.object&[@kwargs[:field]]
76
76
  end
77
77
  end
@@ -25,17 +25,12 @@ class Fluxbit::Form::UploadImageInputComponent < Fluxbit::Form::Component
25
25
  @props = props
26
26
  # @height = height.in?(styles[:height].keys) ? height : :md
27
27
  @label = label_value(label, @object, field, id)
28
- @helper_text = define_helper_text(helper_text, @object, field)
28
+ @helper_text = helper_text.present? ? define_helper_text(helper_text, @object, field) : ''
29
29
  @helper_popover = define_helper_popover(helper_popover, @object, field)
30
30
  @helper_popover_placement = helper_popover_placement
31
- # binding.pry
32
31
  @image_path = image_path || (if @object&.send(@field)&.send("attached?")
33
- @object&.send(@field)&.variant(
34
- resize_to_fit: [
35
- 160, 160
36
- ]
37
- )
38
- end) || image_placeholder
32
+ @object&.send(@field)&.variant(resize_to_fit: [ 160, 160 ])
33
+ end) || image_placeholder || ""
39
34
  @props["class"] = "absolute inset-0 h-full w-full cursor-pointer rounded-md border-gray-300 opacity-0"
40
35
  end
41
36
 
@@ -5,17 +5,5 @@ module Fluxbit
5
5
  def fx_body_class
6
6
  "h-full bg-slate-100 dark:bg-slate-900 dark:text-white"
7
7
  end
8
-
9
- def fx_darkmode_js
10
- <<-SCRIPT.squish
11
- <script>
12
- if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
13
- document.documentElement.classList.add('dark');
14
- } else {
15
- document.documentElement.classList.remove('dark')
16
- }
17
- </script>
18
- SCRIPT
19
- end
20
8
  end
21
9
  end
@@ -0,0 +1,7 @@
1
+ <script>
2
+ if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
3
+ document.documentElement.classList.add('dark');
4
+ } else {
5
+ document.documentElement.classList.remove('dark')
6
+ }
7
+ </script>
@@ -0,0 +1,4 @@
1
+ const execSync = require('child_process').execSync;
2
+ module.exports = {
3
+ content: [`${execSync("bundle show fluxbit").toString().trim()}/**/*.{erb,html,rb}`]
4
+ }
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Fluxbit
4
2
  module ViewComponents
5
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
6
4
  end
7
5
  end
@@ -1,64 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- APPLICATION_LAYOUT_PATH = Rails.root.join("app/views/layouts/application.html.erb")
4
- IMPORTMAP_BINSTUB = Rails.root.join("bin/importmap")
5
- IMPORTMAP_CONFIG_PATH = Rails.root.join("config/importmap.rb")
6
- STIMULUS_PATH = Rails.root.join("app/javascript/controllers/index.js")
3
+ gem_path = Gem.loaded_specs['fluxbit_view_components'].full_gem_path
4
+ template_path = File.join(gem_path, "lib/fluxbit/templates/tailwind.config.js.template")
5
+ darkmode_path = File.join(gem_path, "lib/fluxbit/templates/darkmode.js.template")
6
+ layout_path = Rails.root.join("app/views/layouts/application.html.erb")
7
+ importmap_binstub = Rails.root.join("bin/importmap")
8
+ importmap_config = Rails.root.join("config/importmap.rb")
9
+ stimulus_path = Rails.root.join("app/javascript/controllers/index.js")
10
+ package_json_path = Rails.root.join("package.json")
11
+ tailwind_config_path = Rails.root.join("tailwind.config.js")
12
+ stylesheets_path = Rails.root.join("app/assets/stylesheets/application.tailwind.css")
7
13
 
8
- if APPLICATION_LAYOUT_PATH.exist?
9
- say "Add Fluxbit styles in application layout"
10
- insert_into_file APPLICATION_LAYOUT_PATH.to_s, "\n <%= stylesheet_link_tag \"fluxbit_view_components\" %>", before: /\s*<\/head>/
14
+ say = ->(msg, color = :green) { puts color == :red ? "\e[31m#{msg}\e[0m" : "\e[32m#{msg}\e[0m" }
11
15
 
12
- if File.read(APPLICATION_LAYOUT_PATH).include?("<body>")
13
- say "Add Fluxbit classes and inline styles for <html> in application layout"
14
- gsub_file APPLICATION_LAYOUT_PATH.to_s, "<html", "<html class=\"<%= fluxbit_html_classes %>\" style=\"<%= fluxbit_html_styles %>\""
16
+ Bundler.with_unbundled_env do
17
+ say.call "- Adding TailwindCSS gems..."
18
+ system "bundle add tailwindcss-ruby"
19
+ system "bundle add tailwindcss-rails"
20
+ end
15
21
 
16
- say "Add Fluxbit inline styles for <body> in application layout"
17
- gsub_file APPLICATION_LAYOUT_PATH.to_s, "<body>", "<body style=\"<%= fluxbit_body_styles %>\">"
18
- else
19
- say "<body> tag is not found in application layout.", :red
20
- say " Replace <html> with <html class=\"<%= fluxbit_html_classes %>\" style=\"<%= fluxbit_html_styles %>\"> in your custom layour."
21
- say " Replace <body> with <body style=\"<%= fluxbit_body_styles %>\"> in your custom layour."
22
- end
22
+ say.call "- Running tailwindcss:install..."
23
+ system "#{RbConfig.ruby} ./bin/rails tailwindcss:install"
24
+
25
+ say.call "- Installing Flowbite via npm..."
26
+ system "npm install flowbite --save"
27
+
28
+ say.call "- Copying tailwind.config.js from template..."
29
+ if File.exist?(template_path)
30
+ FileUtils.cp(template_path, tailwind_config_path)
31
+ say.call " tailwind.config.js copied successfully!"
23
32
  else
24
- say "Default application.html.erb is missing!", :red
25
- say " 1. Add <%= stylesheet_link_tag \"fluxbit_view_components\" %> within the <head> tag in your custom layout."
26
- say " 2. Replace <html> with <html class=\"<%= fluxbit_html_classes %>\" style=\"<%= fluxbit_html_styles %>\"> in your custom layour."
27
- say " 3. Replace <body> with <body style=\"<%= fluxbit_body_styles %>\"> in your custom layour."
33
+ say.call " tailwind.config.js.template not found at #{template_path}.", :red
28
34
  end
29
35
 
30
- if IMPORTMAP_BINSTUB.exist?
31
- importmaps = File.read(IMPORTMAP_CONFIG_PATH)
36
+ if stylesheets_path.exist?
37
+ say.call "- Updating CSS with Flowbite imports..."
38
+ content = File.read(stylesheets_path)
39
+ content.prepend("@import \"flowbite/src/themes/default\";\n")
40
+ content << "\n@plugin \"flowbite/plugin\";\n@source \"../../../node_modules/flowbite\";\n@config \"../../../tailwind.config.js\";\n"
41
+ File.write(stylesheets_path, content)
42
+ else
43
+ say.call "⚠️ Couldn't find application.tailwind.css, skipping CSS modifications", :red
44
+ end
32
45
 
33
- unless importmaps.include?("@rails/request.js")
34
- say "Pin @rails/request.js dependency"
35
- run "bin/importmap pin @rails/request.js --download"
36
- end
46
+ if layout_path.exist?
47
+ say.call "- Updating layout to include Fluxbit styles..."
37
48
 
38
- say "Pin fluxbit_view_components"
39
- append_to_file IMPORTMAP_CONFIG_PATH do
40
- %(pin "fluxbit-view-components", to: "fluxbit_view_components.js"\n)
41
- end
42
- else
43
- package_json = File.read(Rails.root.join("package.json"))
49
+ layout_content = File.read(layout_path)
44
50
 
45
- unless package_json.include?("@rails/request.js")
46
- say "Add @rails/request.js dependency"
47
- run "yarn add @rails/request.js"
51
+ if layout_content.include?("<head>")
52
+ darkmode_content = File.read(darkmode_path)
53
+ gsub_file layout_path.to_s, "</head>", "#{darkmode_content}\n</head>"
54
+ say.call " Added darkmode js before </head> tag."
55
+ else
56
+ say.call "</head> tag is not found in application layout.", :red
57
+ say.call " Add darkmode js manually before the </head> tag."
48
58
  end
49
59
 
50
- say "Add fluxbit-view-components package"
51
- run "yarn add fluxbit-view-components"
52
- end
53
60
 
54
- if STIMULUS_PATH.exist?
55
- say "Add Fluxbit Stimulus controllers"
56
- append_to_file STIMULUS_PATH do
57
- "\nimport { registerFluxbitControllers } from \"fluxbit-view-components\"\nregisterFluxbitControllers(Stimulus)\n"
61
+ if layout_content.include?("<body")
62
+ # gsub_file layout_path.to_s, "<html", "<html class=\"<%= fluxbit_html_classes %>\""
63
+ gsub_file layout_path.to_s, "<body>", "<body class=\"<%= fx_body_class %>\">"
64
+ else
65
+ say.call "<body> tag is not found in application layout.", :red
66
+ say.call " Replace <html> and <body> manually as instructed."
58
67
  end
59
68
  else
60
- say "Default Stimulus location is missing: app/javascript/controllers/index.js", :red
61
- say " Add to your Stimulus index.js:"
62
- say " import { registerFluxbitControllers } from \"fluxbit-view-components\""
63
- say " registerFluxbitControllers(Stimulus)"
69
+ say.call "Default application.html.erb is missing!", :red
70
+ say.call " 1. Add stylesheet_link_tag inside <head>"
71
+ say.call " 2. Replace <html> and <body> manually."
64
72
  end
73
+
74
+ say.call "✅ Fluxbit ViewComponents install completed!"
@@ -1,22 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fluxbit/view_components/codemods/v3_slot_setters"
4
-
5
3
  namespace :fluxbit_view_components do
6
4
  desc "Setup Fluxbit::ViewComponents for the app"
7
5
  task :install do
8
6
  system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/install.rb", __dir__)}"
9
7
  end
10
8
 
11
- task detect_legacy_slots: :environment do
12
- ARGV.each { |a| task a.to_sym { } }
13
- custom_paths = ARGV.compact.map { |path| Rails.root.join(path) }
14
- Fluxbit::ViewComponents::Codemods::V3SlotSetters.new(view_path: custom_paths).call
9
+ desc "Copy Fluxbit ViewComponents to the application (default value of destination is app/components/fluxbit)"
10
+ task :copy_components, [ :destination ] do |t, args|
11
+ require "fileutils"
12
+
13
+ gem_path = Gem.loaded_specs["fluxbit_view_components"].full_gem_path
14
+ components_source = File.join(gem_path, "app/components/fluxbit")
15
+ components_destination = args[:destination] || Rails.root.join("app/components/fluxbit")
16
+
17
+ if Dir.exist?(components_source)
18
+ FileUtils.mkdir_p(components_destination)
19
+ FileUtils.cp_r("#{components_source}/.", components_destination)
20
+ puts "✅ Components copied to #{components_destination}"
21
+ else
22
+ puts "❌ Components source directory not found at #{components_source}"
23
+ end
15
24
  end
16
25
 
17
- task migrate_legacy_slots: :environment do
18
- ARGV.each { |a| task a.to_sym { } }
19
- custom_paths = ARGV.compact.map { |path| Rails.root.join(path) }
20
- Fluxbit::ViewComponents::Codemods::V3SlotSetters.new(view_path: custom_paths, migrate: true).call
26
+ desc "Copy Fluxbit ViewComponents Previews to the application (default value of destination is test/components/previews/fluxbit)"
27
+ task :copy_previews, [ :destination ] do |t, args|
28
+ require "fileutils"
29
+
30
+ gem_path = Gem.loaded_specs["fluxbit_view_components"].full_gem_path
31
+ previews_source = File.join(gem_path, "previews/fluxbit")
32
+ previews_destination = args[:destination] || Rails.root.join("test/components/previews/fluxbit")
33
+
34
+ if Dir.exist?(previews_source)
35
+ FileUtils.mkdir_p(previews_destination)
36
+ FileUtils.cp_r("#{previews_source}/.", previews_destination)
37
+ puts "✅ Previews copied to #{previews_destination}"
38
+ else
39
+ puts "❌ Previews source directory not found at #{previews_source}"
40
+ end
21
41
  end
22
42
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluxbit_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arthur Molina
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-04-08 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: anyicon
@@ -142,7 +141,6 @@ dependencies:
142
141
  - - ">="
143
142
  - !ruby/object:Gem::Version
144
143
  version: '0'
145
- description:
146
144
  email:
147
145
  - arthurmolina@gmail.com
148
146
  executables: []
@@ -204,8 +202,9 @@ files:
204
202
  - lib/fluxbit/config/tab_component.rb
205
203
  - lib/fluxbit/config/text_component.rb
206
204
  - lib/fluxbit/config/tooltip_component.rb
205
+ - lib/fluxbit/templates/darkmode.js.template
206
+ - lib/fluxbit/templates/tailwind.config.js.template
207
207
  - lib/fluxbit/view_components.rb
208
- - lib/fluxbit/view_components/codemods/v3_slot_setters.rb
209
208
  - lib/fluxbit/view_components/engine.rb
210
209
  - lib/fluxbit/view_components/version.rb
211
210
  - lib/fluxbit_view_components.rb
@@ -216,7 +215,6 @@ licenses:
216
215
  - MIT
217
216
  metadata:
218
217
  allowed_push_host: https://rubygems.org
219
- post_install_message:
220
218
  rdoc_options: []
221
219
  require_paths:
222
220
  - lib
@@ -231,8 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
229
  - !ruby/object:Gem::Version
232
230
  version: '0'
233
231
  requirements: []
234
- rubygems_version: 3.5.16
235
- signing_key:
232
+ rubygems_version: 3.6.7
236
233
  specification_version: 4
237
234
  summary: ViewComponents for Fluxbit Design System
238
235
  test_files: []
@@ -1,222 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fluxbit
4
- module ViewComponents
5
- # Usage:
6
- #
7
- # Run via rake task:
8
- #
9
- # bin/rails fluxbit_view_components:detect_legacy_slots
10
- # bin/rails fluxbit_view_components:migrate_legacy_slots
11
- # bin/rails fluxbit_view_components:migrate_legacy_slots app/views
12
- #
13
- # Or run via rails console if you need to pass custom paths:
14
- #
15
- # Fluxbit::ViewComponents::Codemods::V3SlotSetters.new(
16
- # view_path: Rails.root.join("app/views"),
17
- # ).call
18
- module Codemods
19
- class V3SlotSetters
20
- TEMPLATE_LANGUAGES = %w[erb slim haml].join(",").freeze
21
- RENDER_REGEX = /render[( ](?<component>\w+(?:::\w+)*)\.new[) ]+(do|\{) \|(?<arg>\w+)\b/ # standard:disable Lint/MixedRegexpCaptureTypes
22
-
23
- Suggestion = Struct.new(:file, :line, :message)
24
-
25
- def initialize(view_component_path: [], view_path: [], migrate: false)
26
- Rails.application.eager_load!
27
-
28
- @view_component_path = view_component_path
29
- @view_path = view_path
30
- @migrate = migrate
31
- end
32
-
33
- def call
34
- puts "Using ViewComponent path: #{view_component_paths.join(", ")}"
35
- puts "Using Views path: #{view_paths.join(", ")}"
36
- puts "#{view_components.size} ViewComponents found"
37
- puts "#{slottable_components.size} ViewComponents using Slots found"
38
- puts "#{view_component_files.size} ViewComponent templates found"
39
- puts "#{view_files.size} view files found"
40
- process_all_files
41
- end
42
-
43
- def process_all_files
44
- all_files.each do |file|
45
- process_file(file)
46
- end
47
- end
48
-
49
- def process_file(file)
50
- @suggestions = []
51
- @suggestions += scan_exact_matches(file)
52
- @suggestions += scan_uncertain_matches(file)
53
-
54
- return unless @suggestions.any?
55
-
56
- puts
57
- puts "File: #{file}"
58
- @suggestions.each do |s|
59
- puts "=> line #{s.line}: #{s.message}"
60
- end
61
- end
62
-
63
- private
64
-
65
- def scan_exact_matches(file)
66
- [].tap do |suggestions|
67
- rendered_components = []
68
- content = File.read(file)
69
-
70
- if (render_match = content.match(RENDER_REGEX))
71
- component = render_match[:component]
72
- arg = render_match[:arg]
73
-
74
- if registered_slots.key?(component.constantize)
75
- used_slots_names = registered_slots[component.constantize]
76
- rendered_components << { component: component, arg: arg, slots: used_slots_names }
77
- end
78
- end
79
-
80
- File.open(file, "r+") do |f|
81
- lines = []
82
- f.each_line do |line|
83
- rendered_components.each do |rendered_component|
84
- arg = rendered_component[:arg]
85
- slots = rendered_component[:slots]
86
-
87
- if (matches = line.scan(/#{arg}\.#{Regexp.union(slots)}/))
88
- matches.each do |match|
89
- new_value = match.gsub("#{arg}.", "#{arg}.with_")
90
- message = if @migrate
91
- "replaced `#{match}` with `#{new_value}`"
92
- else
93
- "probably replace `#{match}` with `#{new_value}`"
94
- end
95
- suggestions << Suggestion.new(file, f.lineno, message)
96
- if @migrate
97
- line.gsub!("#{arg}.", "#{arg}.with_")
98
- end
99
- end
100
- end
101
- end
102
- lines << line
103
- end
104
-
105
- if @migrate
106
- f.rewind
107
- f.write(lines.join)
108
- end
109
- end
110
- end
111
- end
112
-
113
- def scan_uncertain_matches(file)
114
- [].tap do |suggestions|
115
- File.open(file, "r+") do |f|
116
- lines = []
117
- f.each_line do |line|
118
- if (matches = line.scan(/(?<!\s)\.(?<slot>#{Regexp.union(all_registered_slot_names)})\b/))
119
- matches.flatten.each do |match|
120
- next if @suggestions.find { |s| s.file == file && s.line == f.lineno }
121
-
122
- message = if @migrate
123
- "replaced `#{match}` with `with_#{match}`"
124
- else
125
- "maybe replace `#{match}` with `with_#{match}`"
126
- end
127
- suggestions << Suggestion.new(file, f.lineno, message)
128
- if @migrate
129
- line.gsub!(/(?<!\s)\.(#{match})\b/, ".with_\\1")
130
- end
131
- end
132
- end
133
- lines << line
134
- end
135
-
136
- if @migrate
137
- f.rewind
138
- f.write(lines.join)
139
- end
140
- end
141
- end
142
- end
143
-
144
- def view_components
145
- ViewComponent::Base.descendants
146
- end
147
-
148
- def slottable_components
149
- view_components.select do |comp|
150
- comp.registered_slots.any?
151
- end
152
- end
153
-
154
- def registered_slots
155
- @registered_slots ||= {}.tap do |slots|
156
- puts
157
- puts "Detected slots:"
158
- slottable_components.each do |comp|
159
- puts "- `#{comp}` has slots: #{comp.registered_slots.keys.join(", ")}"
160
- slots[comp] = comp.registered_slots.map do |slot_name, slot|
161
- normalized_slot_name(slot_name, slot)
162
- end
163
- end
164
- end
165
- end
166
-
167
- def all_registered_slot_names
168
- @all_registered_slot_names ||= registered_slots.values.flatten.uniq
169
- end
170
-
171
- def view_component_files
172
- Dir.glob(Pathname.new(File.join(view_component_path_glob, "**", "*.{rb,#{TEMPLATE_LANGUAGES}}")))
173
- end
174
-
175
- def view_files
176
- Dir.glob(Pathname.new(File.join(view_path_glob, "**", "*.{#{TEMPLATE_LANGUAGES}}")))
177
- end
178
-
179
- def all_files
180
- view_component_files + view_files
181
- end
182
-
183
- def view_component_paths
184
- @view_component_paths ||= [
185
- Rails.application.config.view_component.view_component_path,
186
- @view_component_path
187
- ].flatten.compact.uniq
188
- end
189
-
190
- def view_component_path_glob
191
- return view_component_paths.first if view_component_paths.size == 1
192
-
193
- "{#{view_component_paths.join(",")}}"
194
- end
195
-
196
- def rails_view_paths
197
- ActionController::Base.view_paths.select do |path|
198
- path.to_s.include?(Rails.root.to_s)
199
- end.map(&:to_s)
200
- end
201
-
202
- def view_paths
203
- @view_paths ||= [
204
- rails_view_paths,
205
- Rails.application.config.view_component.preview_paths,
206
- @view_path
207
- ].flatten.compact.uniq
208
- end
209
-
210
- def view_path_glob
211
- return view_paths.first if view_paths.size == 1
212
-
213
- "{#{view_paths.join(",")}}"
214
- end
215
-
216
- def normalized_slot_name(slot_name, slot)
217
- slot[:collection] ? ActiveSupport::Inflector.singularize(slot_name) : slot_name.to_s
218
- end
219
- end
220
- end
221
- end
222
- end