cubit_components 0.1.0 → 0.2.1

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -43
  3. data/Rakefile +8 -5
  4. data/lib/cubit_components/base_component.rb +48 -19
  5. data/lib/cubit_components/box_component.rb +1 -3
  6. data/lib/cubit_components/configuration.rb +16 -8
  7. data/lib/cubit_components/container_component.rb +11 -0
  8. data/lib/cubit_components/flex_component.rb +17 -0
  9. data/lib/cubit_components/grid_component.rb +16 -0
  10. data/lib/cubit_components/hstack_component.rb +12 -0
  11. data/lib/cubit_components/page_component.rb +11 -27
  12. data/lib/cubit_components/page_container_component.rb +27 -0
  13. data/lib/cubit_components/placeholder_component.rb +36 -0
  14. data/lib/cubit_components/spread_component.rb +9 -54
  15. data/lib/cubit_components/stack_component.rb +7 -25
  16. data/lib/cubit_components/style_dsl.rb +53 -0
  17. data/lib/cubit_components/version.rb +3 -1
  18. data/lib/cubit_components/vstack_component.rb +12 -0
  19. data/lib/cubit_components.rb +11 -24
  20. data/sig/cubit_components.rbs +4 -0
  21. metadata +23 -156
  22. data/MIT-LICENSE +0 -20
  23. data/app/assets/stylesheets/cubit_components/application.css +0 -6
  24. data/app/assets/tailwind/cubit_components/engine.css +0 -4
  25. data/config/routes.rb +0 -2
  26. data/lib/cubit_components/bleed_component.rb +0 -26
  27. data/lib/cubit_components/columns_component.rb +0 -48
  28. data/lib/cubit_components/content_block_component.rb +0 -21
  29. data/lib/cubit_components/engine.rb +0 -14
  30. data/lib/cubit_components/inline_component.rb +0 -19
  31. data/lib/cubit_components/page_block_component.rb +0 -37
  32. data/lib/cubit_components/previews/bleed_component_preview.rb +0 -13
  33. data/lib/cubit_components/previews/box_component_preview.rb +0 -12
  34. data/lib/cubit_components/previews/columns_component_preview.rb +0 -20
  35. data/lib/cubit_components/previews/components_index.rb +0 -28
  36. data/lib/cubit_components/previews/content_block_component_preview.rb +0 -13
  37. data/lib/cubit_components/previews/inline_component_preview.rb +0 -25
  38. data/lib/cubit_components/previews/page_block_component_preview.rb +0 -13
  39. data/lib/cubit_components/previews/page_component_preview.rb +0 -17
  40. data/lib/cubit_components/previews/preview_helper.rb +0 -15
  41. data/lib/cubit_components/previews/spread_component_preview.rb +0 -46
  42. data/lib/cubit_components/previews/stack_component_preview.rb +0 -22
  43. data/lib/cubit_components/previews/tiles_component_preview.rb +0 -20
  44. data/lib/cubit_components/tailwind_generated_classes_chat.txt +0 -127
  45. data/lib/cubit_components/tiles_component.rb +0 -43
  46. data/lib/cubit_components/vattr_validator.rb +0 -53
  47. data/lib/generators/cubit/install/install_generator.rb +0 -63
  48. data/lib/tasks/cubit_components_tasks.rake +0 -4
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class StackComponentPreview < ViewComponent::Preview
4
- # Shows default classes (gap-0 text-left)
5
- def default
6
- render(CubitComponents::StackComponent.new) do
7
- tag.div("Item 1", class: "border") +
8
- tag.div("Item 2", class: "border") +
9
- tag.div("Item 3", class: "border")
10
- end
11
- end
12
-
13
- # gap-medium
14
- # Overrides gap utility
15
- def custom_gap
16
- render(CubitComponents::StackComponent.new(vattrs: %w[gap-medium])) do
17
- tag.div("Medium gap", class: "border") +
18
- tag.div("Medium gap", class: "border") +
19
- tag.div("Medium gap", class: "border")
20
- end
21
- end
22
- end
@@ -1,20 +0,0 @@
1
- class TilesComponentPreview < ViewComponent::Preview
2
- # Default preview: renders three simple tiles.
3
- def default
4
- render(CubitComponents::TilesComponent.new(vattrs: ["grid-cols-5"])) do
5
- tag.div("Tile 1", class: "border p-2") +
6
- tag.div("Tile 2", class: "border p-2") +
7
- tag.div("Tile 3", class: "border p-2")
8
- end
9
- end
10
-
11
- # Preview with custom grid columns and gap overrides.
12
- def custom_grid_and_gap
13
- render(CubitComponents::TilesComponent.new(vattrs: ["md:grid-cols-3", "gap-4"])) do
14
- tag.div("Tile 1", class: "border p-2") +
15
- tag.div("Tile 2", class: "border p-2") +
16
- tag.div("Tile 3", class: "border p-2") +
17
- tag.div("Tile 4", class: "border p-2")
18
- end
19
- end
20
- end
@@ -1,127 +0,0 @@
1
-
2
-
3
- Well, the thing I didn't mention is that this is a Rails engine/gem, and the host app can define other custom breakpoints and I need to have tailwind capture those too. I don't think any of the solutions you mentioned can work with that. Correct me if I'm wrong.
4
-
5
- You're absolutely right! Since this is a Rails engine/gem where the host app can define arbitrary custom breakpoints, none of my previous solutions would work. This is a tricky problem because Tailwind needs to know about classes at build time, but you don't know what breakpoints the host app will define.
6
-
7
- Here are some approaches that could work:
8
-
9
- ## Solution 1: Host App Responsibility (Recommended)
10
-
11
- Document that host apps need to safelist the dynamic classes themselves:
12
-
13
- ```ruby
14
- # In your gem's documentation/README
15
- #
16
- # If you use custom breakpoints with CubitComponents::ColumnsComponent,
17
- # add them to your tailwind.config.js safelist:
18
- #
19
- # safelist: [
20
- # { pattern: /^[a-z]+:flex-row$/ },
21
- # // or explicitly:
22
- # 'custom-breakpoint:flex-row',
23
- # 'another-breakpoint:flex-row'
24
- # ]
25
- ```
26
-
27
- ## Solution 2: Configuration Hook
28
-
29
- Provide a configuration option in your engine where host apps can register their breakpoints:
30
-
31
- ```ruby
32
- # In your engine
33
- module CubitComponents
34
- class << self
35
- attr_accessor :breakpoints
36
-
37
- def configure
38
- yield self
39
- end
40
- end
41
-
42
- self.breakpoints = %w[sm md lg xl 2xl]
43
- end
44
-
45
- # Host app would do:
46
- CubitComponents.configure do |config|
47
- config.breakpoints = %w[sm md lg xl 2xl tablet desktop custom]
48
- end
49
- ```
50
-
51
- Then provide a rake task or generator that outputs the necessary Tailwind config:
52
-
53
- ```ruby
54
- # lib/tasks/cubit_components_tasks.rake
55
- namespace :cubit_components do
56
- desc "Generate Tailwind safelist for CubitComponents"
57
- task :tailwind_safelist => :environment do
58
- breakpoints = CubitComponents.breakpoints
59
- safelist = breakpoints.map { |bp| "#{bp}:flex-row" }
60
-
61
- puts "Add this to your tailwind.config.js safelist:"
62
- puts safelist.to_json
63
- end
64
- end
65
- ```
66
-
67
- ## Solution 3: Always Use Style Attribute
68
-
69
- For truly dynamic values, you might need to bypass Tailwind classes entirely:
70
-
71
- ```ruby
72
- private
73
-
74
- def build_layout_classes(user)
75
- base = %w[flex]
76
- base << if @collapse_below && @reverse
77
- "flex-col-reverse"
78
- elsif @collapse_below
79
- "flex-col"
80
- else
81
- "flex-row"
82
- end
83
-
84
- # Store the breakpoint for later use in style attribute
85
- @responsive_style = @collapse_below
86
-
87
- (base + user.to_s.split).join(" ")
88
- end
89
-
90
- # Then in your component's template, use a media query
91
- # This would require passing the actual breakpoint value (e.g., "768px")
92
- ```
93
-
94
- ## Solution 4: JIT Arbitrary Value Support
95
-
96
- If the host app uses Tailwind JIT mode, you could potentially use arbitrary values, but this still requires the host app to configure their breakpoints properly.
97
-
98
- ## Recommendation
99
-
100
- I'd recommend **Solution 1** - making it the host app's responsibility to safelist the classes they need. This is the most common pattern for UI component libraries. You could make it easier by:
101
-
102
- 1. Providing clear documentation
103
- 2. Offering a generator that creates a partial Tailwind config
104
- 3. Providing common patterns they can use
105
-
106
- Example generator:
107
- ```ruby
108
- # lib/generators/cubit_components/tailwind_generator.rb
109
- module CubitComponents
110
- class TailwindGenerator < Rails::Generators::Base
111
- def create_safelist_config
112
- create_file "config/cubit_components_tailwind.js", <<~JS
113
- // Add this to your tailwind.config.js safelist array
114
- module.exports = {
115
- // Safelist pattern for all breakpoint:flex-row combinations
116
- pattern: /^[a-z0-9]+:flex-row$/,
117
-
118
- // Or if you prefer explicit listing:
119
- // 'your-custom-breakpoint:flex-row',
120
- }
121
- JS
122
- end
123
- end
124
- end
125
- ```
126
-
127
- This way, you're being transparent about the requirement while giving host apps full control over their Tailwind configuration.
@@ -1,43 +0,0 @@
1
- module CubitComponents
2
- class TilesComponent < BaseComponent
3
- # Allowed vattrs for grid columns and gap utilities.
4
- VALID_VATTRS = Set.new(
5
- begin
6
- valid = []
7
- breakpoints = ["", "sm:", "md:", "lg:", "xl:"]
8
- cols = %w[none 1 2 3 4 5]
9
- breakpoints.each do |bp|
10
- cols.each { |col| valid << "#{bp}grid-cols-#{col}" }
11
- end
12
- gap_values = %w[0 1 2 3 4 5]
13
- gap_values.each { |g| valid << "gap-#{g}" }
14
- valid
15
- end
16
- ).freeze
17
-
18
- DEFAULT_VATTRS = %w[grid-cols-1 gap-0].freeze
19
-
20
- def initialize(element: "div", classes: "", vattrs: [])
21
- # Merge defaults (grid cols and gap) with any provided overrides.
22
- vattrs = merge_default_vattrs(Array(vattrs))
23
- # Ensure grid display is always present.
24
- classes = [classes, "grid"].join(" ").strip
25
- super
26
- end
27
-
28
- private
29
-
30
- def merge_default_vattrs(custom)
31
- result = DEFAULT_VATTRS.dup
32
- custom.each do |attr|
33
- if attr.start_with?("grid-cols-")
34
- result.reject! { |v| v.start_with?("grid-cols-") }
35
- elsif attr.start_with?("gap-")
36
- result.reject! { |v| v.start_with?("gap-") }
37
- end
38
- result << attr
39
- end
40
- result
41
- end
42
- end
43
- end
@@ -1,53 +0,0 @@
1
- module CubitComponents
2
- module VattrValidator
3
- extend ActiveSupport::Concern
4
-
5
- class InvalidVattrError < ArgumentError; end
6
-
7
- private
8
-
9
- def validate_vattrs(vattrs, allowed_vattrs, component_name: self.class.name)
10
- return if Rails.env.production?
11
-
12
- invalid_vattrs = Array(vattrs) - Array(allowed_vattrs)
13
- return if invalid_vattrs.empty?
14
-
15
- case CubitComponents.configuration.invalid_vattr_behavior
16
- when :raise
17
- handle_invalid_vattrs_with_exception(invalid_vattrs, allowed_vattrs, component_name)
18
- when :warn
19
- handle_invalid_vattrs_with_warning(invalid_vattrs, allowed_vattrs, component_name)
20
- when :silent
21
- # Do nothing
22
- end
23
- end
24
-
25
- def handle_invalid_vattrs_with_exception(invalid_vattrs, allowed_vattrs, component_name)
26
- message = build_error_message(invalid_vattrs, allowed_vattrs, component_name)
27
- raise InvalidVattrError, message
28
- end
29
-
30
- def handle_invalid_vattrs_with_warning(invalid_vattrs, allowed_vattrs, component_name)
31
- message = build_error_message(invalid_vattrs, allowed_vattrs, component_name)
32
-
33
- # Use uplevel to show the warning at the call site
34
- Kernel.warn("\n#{message}", uplevel: 3)
35
-
36
- # Also log with color if Rails logger is available
37
- if defined?(Rails.logger) && Rails.logger
38
- Rails.logger.warn("\e[33m#{message}\e[0m")
39
- end
40
- end
41
-
42
- def build_error_message(invalid_vattrs, allowed_vattrs, component_name)
43
- <<~MESSAGE
44
- ⚠️ [CubitComponents] Invalid vattrs for #{component_name}:
45
- Invalid: #{invalid_vattrs.join(", ")}
46
-
47
- Allowed vattrs: #{allowed_vattrs.join(", ")}
48
-
49
- These vattrs will have no effect on the component.
50
- MESSAGE
51
- end
52
- end
53
- end
@@ -1,63 +0,0 @@
1
- require "rails/generators" if defined?(Rails::Generators)
2
-
3
- module Cubit
4
- class InstallGenerator < Rails::Generators::Base
5
- source_root File.expand_path("../templates", __FILE__)
6
-
7
- def install
8
- say "Installing Cubit Components...", :green
9
-
10
- add_tailwind_import
11
- verify_installation
12
- end
13
-
14
- private
15
-
16
- def add_tailwind_import
17
- target_file = "app/assets/tailwind/application.css"
18
-
19
- if !File.exist?(target_file)
20
- say "Could not find Tailwind CSS file to import into. Please manually add:", :red
21
- say '@import "../builds/tailwind/cubit_components.css";', :yellow
22
- say <<~TW_VERSION_WARNING, :red
23
- ⚠️ Warning: Tailwind CSS file not found at #{target_file}.
24
- Please ensure you have Tailwind CSS version 4.0 or higher installed and configured correctly and that you are using a CSS config file rather than `tailwind.config.js`.
25
- TW_VERSION_WARNING
26
- return
27
- end
28
-
29
- content = File.read(target_file)
30
- import_line = '@import "../builds/tailwind/cubit_components.css";'
31
-
32
- if content.include?(import_line)
33
- say "CubitComponents is already imported in #{target_file}", :yellow
34
- return
35
- end
36
-
37
- if content.include?('@import "tailwindcss";')
38
- inject_into_file target_file, after: '@import "tailwindcss";' do
39
- "\n#{import_line}"
40
- end
41
- else
42
- append_to_file target_file do
43
- "\n#{import_line}"
44
- end
45
- end
46
-
47
- say "Added CubitComponents import to #{target_file}", :green
48
- end
49
-
50
- def verify_installation
51
- say "\nVerifying installation...", :blue
52
-
53
- # Check if any Tailwind file contains our import
54
- tailwind_files = Dir.glob("app/assets/{stylesheets,tailwind}/*.css")
55
- unless tailwind_files.any? { |f| File.read(f).include?("cubit_components") }
56
- say "⚠️ Warning: Cubit Components import not found in any CSS files", :red
57
- say "Please manually add @import \"../builds/tailwind/cubit_components.css\"; to your main CSS file", :yellow
58
- end
59
-
60
- say "✅ Verification complete", :green
61
- end
62
- end
63
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :cubit_components do
3
- # # Task goes here
4
- # end