shadcn-rails 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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -2
  3. data/README.md +102 -1398
  4. data/__mocks__/@floating-ui/dom.js +67 -0
  5. data/app/assets/javascripts/shadcn/controllers/base_menu_controller.js +266 -0
  6. data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +34 -8
  7. data/app/assets/javascripts/shadcn/controllers/command_controller.js +5 -1
  8. data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +64 -135
  9. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +56 -186
  10. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +29 -55
  11. data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +10 -7
  12. data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +10 -6
  13. data/app/assets/javascripts/shadcn/controllers/popover_controller.js +35 -60
  14. data/app/assets/javascripts/shadcn/controllers/select_controller.js +37 -17
  15. data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +24 -14
  16. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +28 -59
  17. data/app/assets/javascripts/shadcn/index.js +9 -1
  18. data/app/assets/javascripts/shadcn/utils/floating.js +179 -0
  19. data/app/assets/stylesheets/shadcn/base.css +32 -0
  20. data/app/assets/stylesheets/shadcn/components.css +12 -0
  21. data/app/components/shadcn/accordion_component.html.erb +8 -0
  22. data/app/components/shadcn/accordion_component.rb +6 -15
  23. data/app/components/shadcn/alert_component.html.erb +6 -0
  24. data/app/components/shadcn/alert_component.rb +0 -18
  25. data/app/components/shadcn/alert_dialog_component.html.erb +12 -0
  26. data/app/components/shadcn/alert_dialog_component.rb +7 -27
  27. data/app/components/shadcn/aspect_ratio_component.html.erb +7 -0
  28. data/app/components/shadcn/aspect_ratio_component.rb +4 -19
  29. data/app/components/shadcn/avatar_component.html.erb +20 -0
  30. data/app/components/shadcn/avatar_component.rb +8 -36
  31. data/app/components/shadcn/badge_component.html.erb +1 -0
  32. data/app/components/shadcn/badge_component.rb +0 -11
  33. data/app/components/shadcn/base_component.rb +15 -2
  34. data/app/components/shadcn/breadcrumb_component.html.erb +5 -0
  35. data/app/components/shadcn/breadcrumb_component.rb +6 -16
  36. data/app/components/shadcn/button_component.html.erb +18 -0
  37. data/app/components/shadcn/button_component.rb +1 -41
  38. data/app/components/shadcn/card_component.html.erb +8 -0
  39. data/app/components/shadcn/card_component.rb +2 -6
  40. data/app/components/shadcn/checkbox_component.html.erb +32 -0
  41. data/app/components/shadcn/checkbox_component.rb +4 -43
  42. data/app/components/shadcn/collapsible_component.html.erb +8 -0
  43. data/app/components/shadcn/collapsible_component.rb +6 -15
  44. data/app/components/shadcn/command_list_component.rb +29 -14
  45. data/app/components/shadcn/context_menu_checkbox_item_component.rb +76 -0
  46. data/app/components/shadcn/context_menu_component.html.erb +11 -0
  47. data/app/components/shadcn/context_menu_component.rb +6 -26
  48. data/app/components/shadcn/context_menu_content_component.rb +37 -14
  49. data/app/components/shadcn/context_menu_item_component.rb +3 -2
  50. data/app/components/shadcn/context_menu_radio_group_component.rb +42 -0
  51. data/app/components/shadcn/context_menu_radio_item_component.rb +76 -0
  52. data/app/components/shadcn/dialog_component.html.erb +14 -0
  53. data/app/components/shadcn/dialog_component.rb +8 -29
  54. data/app/components/shadcn/drawer_component.html.erb +12 -0
  55. data/app/components/shadcn/drawer_component.rb +7 -27
  56. data/app/components/shadcn/dropdown_menu_checkbox_item_component.rb +76 -0
  57. data/app/components/shadcn/dropdown_menu_component.html.erb +14 -0
  58. data/app/components/shadcn/dropdown_menu_component.rb +9 -29
  59. data/app/components/shadcn/dropdown_menu_content_component.rb +45 -16
  60. data/app/components/shadcn/dropdown_menu_radio_group_component.rb +42 -0
  61. data/app/components/shadcn/dropdown_menu_radio_item_component.rb +76 -0
  62. data/app/components/shadcn/field_component.rb +7 -8
  63. data/app/components/shadcn/hover_card_component.html.erb +12 -0
  64. data/app/components/shadcn/hover_card_component.rb +7 -26
  65. data/app/components/shadcn/input_component.html.erb +18 -0
  66. data/app/components/shadcn/input_component.rb +2 -27
  67. data/app/components/shadcn/input_otp_component.rb +3 -3
  68. data/app/components/shadcn/kbd_component.html.erb +1 -0
  69. data/app/components/shadcn/kbd_component.rb +3 -10
  70. data/app/components/shadcn/label_component.html.erb +3 -0
  71. data/app/components/shadcn/label_component.rb +2 -18
  72. data/app/components/shadcn/menubar_component.html.erb +6 -0
  73. data/app/components/shadcn/menubar_component.rb +4 -15
  74. data/app/components/shadcn/menubar_content_component.rb +45 -20
  75. data/app/components/shadcn/menubar_sub_content_component.rb +21 -8
  76. data/app/components/shadcn/native_select_component.html.erb +22 -0
  77. data/app/components/shadcn/native_select_component.rb +9 -39
  78. data/app/components/shadcn/navigation_menu_component.html.erb +6 -0
  79. data/app/components/shadcn/navigation_menu_component.rb +4 -15
  80. data/app/components/shadcn/pagination_component.html.erb +5 -0
  81. data/app/components/shadcn/pagination_component.rb +11 -15
  82. data/app/components/shadcn/popover_component.html.erb +15 -0
  83. data/app/components/shadcn/popover_component.rb +10 -30
  84. data/app/components/shadcn/progress_component.html.erb +13 -0
  85. data/app/components/shadcn/progress_component.rb +6 -26
  86. data/app/components/shadcn/radio_group_component.html.erb +8 -0
  87. data/app/components/shadcn/radio_group_component.rb +12 -26
  88. data/app/components/shadcn/radio_group_item_component.rb +32 -6
  89. data/app/components/shadcn/resizable_panel_group_component.rb +27 -16
  90. data/app/components/shadcn/scroll_area_component.html.erb +7 -0
  91. data/app/components/shadcn/scroll_area_component.rb +4 -16
  92. data/app/components/shadcn/select_component.html.erb +46 -0
  93. data/app/components/shadcn/select_component.rb +29 -86
  94. data/app/components/shadcn/separator_component.html.erb +5 -0
  95. data/app/components/shadcn/separator_component.rb +6 -14
  96. data/app/components/shadcn/sheet_component.html.erb +12 -0
  97. data/app/components/shadcn/sheet_component.rb +7 -27
  98. data/app/components/shadcn/sidebar_component.rb +2 -2
  99. data/app/components/shadcn/skeleton_component.html.erb +1 -0
  100. data/app/components/shadcn/skeleton_component.rb +4 -2
  101. data/app/components/shadcn/slider_component.html.erb +12 -0
  102. data/app/components/shadcn/slider_component.rb +2 -21
  103. data/app/components/shadcn/spinner_component.html.erb +18 -0
  104. data/app/components/shadcn/spinner_component.rb +2 -30
  105. data/app/components/shadcn/switch_component.html.erb +72 -0
  106. data/app/components/shadcn/switch_component.rb +4 -82
  107. data/app/components/shadcn/table_component.html.erb +9 -0
  108. data/app/components/shadcn/table_component.rb +2 -10
  109. data/app/components/shadcn/tabs_component.html.erb +8 -0
  110. data/app/components/shadcn/tabs_component.rb +4 -17
  111. data/app/components/shadcn/textarea_component.html.erb +13 -0
  112. data/app/components/shadcn/textarea_component.rb +6 -22
  113. data/app/components/shadcn/toast_component.html.erb +36 -0
  114. data/app/components/shadcn/toast_component.rb +6 -54
  115. data/app/components/shadcn/toggle_component.html.erb +12 -0
  116. data/app/components/shadcn/toggle_component.rb +6 -21
  117. data/app/components/shadcn/toggle_group_component.html.erb +14 -0
  118. data/app/components/shadcn/toggle_group_component.rb +6 -29
  119. data/app/components/shadcn/tooltip_component.html.erb +20 -0
  120. data/app/components/shadcn/tooltip_component.rb +13 -38
  121. data/lib/generators/shadcn/add/USAGE +24 -0
  122. data/lib/generators/shadcn/add/add_generator.rb +279 -0
  123. data/lib/generators/shadcn/install/USAGE +22 -0
  124. data/lib/generators/shadcn/install/install_generator.rb +8 -3
  125. data/lib/generators/shadcn/install/templates/initializer.rb.tt +7 -27
  126. data/lib/generators/shadcn/install/templates/shadcn.yml.tt +15 -31
  127. data/lib/shadcn/rails/version.rb +1 -1
  128. metadata +54 -42
  129. data/.dockerignore +0 -40
  130. data/CLAUDE.md +0 -463
  131. data/PROGRESS.md +0 -485
  132. data/Rakefile +0 -29
  133. data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +0 -13
  134. data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +0 -46
  135. data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +0 -111
  136. data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +0 -27
  137. data/__tests__/controllers/accordion_controller.test.js +0 -904
  138. data/__tests__/controllers/calendar_controller.test.js +0 -1370
  139. data/__tests__/controllers/carousel_controller.test.js +0 -912
  140. data/__tests__/controllers/checkbox_controller.test.js +0 -454
  141. data/__tests__/controllers/collapsible_controller.test.js +0 -407
  142. data/__tests__/controllers/combobox_controller.test.js +0 -966
  143. data/__tests__/controllers/context_menu_controller.test.js +0 -627
  144. data/__tests__/controllers/date_picker_controller.test.js +0 -636
  145. data/__tests__/controllers/dialog_controller.test.js +0 -878
  146. data/__tests__/controllers/drawer_controller.test.js +0 -995
  147. data/__tests__/controllers/menubar_controller.test.js +0 -736
  148. data/__tests__/controllers/navigation_menu_controller.test.js +0 -598
  149. data/__tests__/controllers/popover_controller.test.js +0 -1007
  150. data/__tests__/controllers/radio_group_controller.test.js +0 -640
  151. data/__tests__/controllers/resizable_controller.test.js +0 -680
  152. data/__tests__/controllers/select_controller.test.js +0 -674
  153. data/__tests__/controllers/sheet_controller.test.js +0 -986
  154. data/__tests__/controllers/slider_controller.test.js +0 -1036
  155. data/__tests__/controllers/switch_controller.test.js +0 -424
  156. data/__tests__/controllers/tabs_controller.test.js +0 -907
  157. data/__tests__/controllers/toggle_group_controller.test.js +0 -839
  158. data/__tests__/controllers/tooltip_controller.test.js +0 -808
  159. data/__tests__/helpers/stimulus-test-helper.js +0 -203
  160. data/babel.config.cjs +0 -5
  161. data/bin/console +0 -11
  162. data/bin/setup +0 -8
  163. data/jest.config.js +0 -19
  164. data/jest.setup.js +0 -8
  165. data/lib/generators/shadcn/component/component_generator.rb +0 -188
  166. data/lib/generators/shadcn/theme/theme_generator.rb +0 -128
  167. data/package-lock.json +0 -7415
  168. data/package.json +0 -68
  169. data/rollup.config.js +0 -29
@@ -18,7 +18,7 @@ module Shadcn
18
18
  # <% end %>
19
19
  #
20
20
  class TooltipComponent < BaseComponent
21
- CONTENT_CLASSES = "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
21
+ CONTENT_CLASSES = "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground whitespace-nowrap animate-tooltip-in data-[state=closed]:animate-tooltip-out"
22
22
 
23
23
  # @param text [String] Tooltip text content
24
24
  # @param side [Symbol] Side to show tooltip (:top, :right, :bottom, :left)
@@ -41,49 +41,24 @@ module Shadcn
41
41
  super(**options)
42
42
  end
43
43
 
44
- def call
45
- content_tag(:span, tooltip_structure, tooltip_attributes)
46
- end
47
-
48
44
  private
49
45
 
50
- def tooltip_structure
51
- safe_join([
52
- trigger_wrapper,
53
- tooltip_content_element
54
- ])
55
- end
56
-
57
- def trigger_wrapper
58
- content_tag(:span, content, {
59
- "data-shadcn--tooltip-target": "trigger",
60
- "data-action": "mouseenter->shadcn--tooltip#show mouseleave->shadcn--tooltip#hide focus->shadcn--tooltip#show blur->shadcn--tooltip#hide"
61
- })
46
+ def tooltip_classes
47
+ cn("relative inline-block", class_name)
62
48
  end
63
49
 
64
- def tooltip_content_element
65
- content_tag(:div, @tooltip_content, {
66
- class: CONTENT_CLASSES,
67
- role: "tooltip",
68
- "data-shadcn--tooltip-target": "content",
69
- "data-side": @side.to_s,
70
- "data-state": "closed",
71
- hidden: true
72
- })
50
+ def tooltip_data_attrs
51
+ {
52
+ controller: "shadcn--tooltip",
53
+ "shadcn--tooltip-side-value": @side.to_s,
54
+ "shadcn--tooltip-align-value": @align.to_s,
55
+ "shadcn--tooltip-delay-value": @delay_duration,
56
+ "shadcn--tooltip-skip-delay-value": @skip_delay_duration
57
+ }
73
58
  end
74
59
 
75
- def tooltip_attributes
76
- attrs = {
77
- class: cn("relative inline-block", class_name),
78
- "data-controller": "shadcn--tooltip",
79
- "data-shadcn--tooltip-side-value": @side.to_s,
80
- "data-shadcn--tooltip-align-value": @align.to_s,
81
- "data-shadcn--tooltip-delay-value": @delay_duration,
82
- "data-shadcn--tooltip-skip-delay-value": @skip_delay_duration
83
- }
84
- attrs.merge!(html_options)
85
- attrs.merge!(build_data)
86
- attrs.compact
60
+ def tooltip_content_classes
61
+ CONTENT_CLASSES
87
62
  end
88
63
  end
89
64
  end
@@ -0,0 +1,24 @@
1
+ Description:
2
+ Adds shadcn components to your Rails application for customization.
3
+
4
+ This copies the component source code into your app so you can modify it.
5
+ Local components take precedence over the gem's built-in components.
6
+
7
+ Examples:
8
+ rails generate shadcn:add button
9
+ rails generate shadcn:add button card dialog
10
+ rails generate shadcn:add dialog --exclude-controllers
11
+ rails generate shadcn:add --all
12
+ rails generate shadcn:add --list
13
+
14
+ This will create:
15
+ app/components/shadcn/button_component.rb
16
+ app/javascript/controllers/shadcn/dialog_controller.js (if applicable)
17
+
18
+ Options:
19
+ --list List all available components
20
+ --all Add all available components
21
+ --include-controllers Include Stimulus controllers (default: true)
22
+ --exclude-controllers Exclude Stimulus controllers
23
+ --force Overwrite existing files
24
+ --path=PATH Path for components (default: app/components)
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/base"
5
+
6
+ module Shadcn
7
+ module Generators
8
+ # Generator for adding shadcn components to your application
9
+ # Usage: rails generate shadcn:add button
10
+ # rails generate shadcn:add button card dialog
11
+ # rails generate shadcn:add button --exclude-controllers
12
+ class AddGenerator < ::Rails::Generators::Base
13
+ source_root File.expand_path("templates", __dir__)
14
+
15
+ argument :components, type: :array, default: [], banner: "component [component ...]"
16
+
17
+ class_option :all, type: :boolean, default: false,
18
+ desc: "Add all available components"
19
+ class_option :list, type: :boolean, default: false,
20
+ desc: "List all available components"
21
+ class_option :include_controllers, type: :boolean, default: true,
22
+ desc: "Include Stimulus controllers (default: true)"
23
+ class_option :exclude_controllers, type: :boolean, default: false,
24
+ desc: "Exclude Stimulus controllers"
25
+ class_option :force, type: :boolean, default: false,
26
+ desc: "Overwrite existing files"
27
+ class_option :path, type: :string, default: "app/components",
28
+ desc: "Path for components (default: app/components)"
29
+
30
+ desc "Adds shadcn components to your application for customization"
31
+
32
+ # Map component names to their files
33
+ COMPONENT_FILES = {
34
+ "accordion" => { component: "accordion_component.rb", controller: "accordion_controller.js" },
35
+ "alert" => { component: "alert_component.rb", controller: nil },
36
+ "alert_dialog" => { component: "alert_dialog_component.rb", controller: "dialog_controller.js" },
37
+ "aspect_ratio" => { component: "aspect_ratio_component.rb", controller: nil },
38
+ "avatar" => { component: "avatar_component.rb", controller: "avatar_controller.js" },
39
+ "badge" => { component: "badge_component.rb", controller: nil },
40
+ "breadcrumb" => { component: "breadcrumb_component.rb", controller: nil },
41
+ "button" => { component: "button_component.rb", controller: nil },
42
+ "button_group" => { component: "button_group_component.rb", controller: nil },
43
+ "calendar" => { component: "calendar_component.rb", controller: "calendar_controller.js" },
44
+ "card" => { component: "card_component.rb", controller: nil },
45
+ "carousel" => { component: "carousel_component.rb", controller: "carousel_controller.js" },
46
+ "checkbox" => { component: "checkbox_component.rb", controller: "checkbox_controller.js" },
47
+ "collapsible" => { component: "collapsible_component.rb", controller: "collapsible_controller.js" },
48
+ "combobox" => { component: "combobox_component.rb", controller: "combobox_controller.js" },
49
+ "command" => { component: "command_component.rb", controller: "command_controller.js" },
50
+ "context_menu" => { component: "context_menu_component.rb", controller: "context_menu_controller.js" },
51
+ "date_picker" => { component: "date_picker_component.rb", controller: "date_picker_controller.js" },
52
+ "dialog" => { component: "dialog_component.rb", controller: "dialog_controller.js" },
53
+ "drawer" => { component: "drawer_component.rb", controller: "drawer_controller.js" },
54
+ "dropdown_menu" => { component: "dropdown_menu_component.rb", controller: "dropdown_controller.js" },
55
+ "field" => { component: "field_component.rb", controller: nil },
56
+ "hover_card" => { component: "hover_card_component.rb", controller: "hover_card_controller.js" },
57
+ "input" => { component: "input_component.rb", controller: nil },
58
+ "input_group" => { component: "input_group_component.rb", controller: nil },
59
+ "input_otp" => { component: "input_otp_component.rb", controller: "input_otp_controller.js" },
60
+ "kbd" => { component: "kbd_component.rb", controller: nil },
61
+ "label" => { component: "label_component.rb", controller: nil },
62
+ "menubar" => { component: "menubar_component.rb", controller: "menubar_controller.js" },
63
+ "native_select" => { component: "native_select_component.rb", controller: nil },
64
+ "navigation_menu" => { component: "navigation_menu_component.rb", controller: "navigation_menu_controller.js" },
65
+ "pagination" => { component: "pagination_component.rb", controller: nil },
66
+ "popover" => { component: "popover_component.rb", controller: "popover_controller.js" },
67
+ "progress" => { component: "progress_component.rb", controller: nil },
68
+ "radio_group" => { component: "radio_group_component.rb", controller: "radio_group_controller.js" },
69
+ "resizable" => { component: "resizable_panel_group_component.rb", controller: "resizable_controller.js" },
70
+ "scroll_area" => { component: "scroll_area_component.rb", controller: "scroll_area_controller.js" },
71
+ "select" => { component: "select_component.rb", controller: "select_controller.js" },
72
+ "separator" => { component: "separator_component.rb", controller: nil },
73
+ "sheet" => { component: "sheet_component.rb", controller: "sheet_controller.js" },
74
+ "sidebar" => { component: "sidebar_component.rb", controller: "sidebar_controller.js" },
75
+ "skeleton" => { component: "skeleton_component.rb", controller: nil },
76
+ "slider" => { component: "slider_component.rb", controller: "slider_controller.js" },
77
+ "spinner" => { component: "spinner_component.rb", controller: nil },
78
+ "switch" => { component: "switch_component.rb", controller: "switch_controller.js" },
79
+ "table" => { component: "table_component.rb", controller: nil },
80
+ "tabs" => { component: "tabs_component.rb", controller: "tabs_controller.js" },
81
+ "textarea" => { component: "textarea_component.rb", controller: nil },
82
+ "toast" => { component: "toast_component.rb", controller: "toast_controller.js" },
83
+ "toggle" => { component: "toggle_component.rb", controller: "toggle_controller.js" },
84
+ "toggle_group" => { component: "toggle_group_component.rb", controller: "toggle_group_controller.js" },
85
+ "tooltip" => { component: "tooltip_component.rb", controller: "tooltip_controller.js" },
86
+ "typography" => { component: "typography_component.rb", controller: nil }
87
+ }.freeze
88
+
89
+ def validate_components
90
+ if options[:list]
91
+ display_available_components
92
+ exit 0
93
+ end
94
+
95
+ if options[:all]
96
+ @components_to_add = COMPONENT_FILES.keys
97
+ else
98
+ validate_requested_components
99
+ @components_to_add = components
100
+ end
101
+ end
102
+
103
+ def display_plan
104
+ say ""
105
+ say "Adding #{@components_to_add.length} component(s):", :green
106
+ @components_to_add.each { |c| say " - #{c}" }
107
+ if include_controllers?
108
+ controllers = @components_to_add.count { |c| COMPONENT_FILES[c][:controller] }
109
+ say ""
110
+ say "Including #{controllers} Stimulus controller(s)", :cyan if controllers > 0
111
+ end
112
+ say ""
113
+ end
114
+
115
+ def add_components
116
+ @components_to_add.each do |component|
117
+ add_component(component)
118
+ end
119
+ end
120
+
121
+ def display_post_add_message
122
+ say ""
123
+ say "=" * 60, :green
124
+ say " Components added successfully!", :green
125
+ say "=" * 60, :green
126
+ say ""
127
+ say "Components are now in your application:", :yellow
128
+ say " - Ruby components: #{options[:path]}/shadcn/"
129
+ if include_controllers?
130
+ say " - Stimulus controllers: app/javascript/controllers/shadcn/"
131
+ end
132
+ say ""
133
+ say "These local files will take precedence over the gem's components."
134
+ say "You can now customize them as needed."
135
+ say ""
136
+ if include_controllers?
137
+ say "Note: Register the controllers in your Stimulus application:", :cyan
138
+ say ""
139
+ say " // In app/javascript/controllers/index.js"
140
+ say " import ShadcnDialog from \"./shadcn/dialog_controller\""
141
+ say " application.register(\"shadcn--dialog\", ShadcnDialog)"
142
+ say ""
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def display_available_components
149
+ say ""
150
+ say "Available shadcn components:", :green
151
+ say ""
152
+
153
+ # Group by category
154
+ categories = {
155
+ "Buttons & Actions" => %w[button button_group toggle toggle_group],
156
+ "Form Inputs" => %w[checkbox field input input_group input_otp label native_select radio_group select slider switch textarea],
157
+ "Data Display" => %w[avatar badge card kbd progress skeleton spinner table typography aspect_ratio scroll_area],
158
+ "Feedback" => %w[alert toast tooltip],
159
+ "Overlays" => %w[alert_dialog dialog drawer dropdown_menu hover_card popover sheet context_menu],
160
+ "Navigation" => %w[accordion breadcrumb collapsible menubar navigation_menu pagination separator tabs resizable],
161
+ "Advanced" => %w[calendar carousel combobox command date_picker sidebar]
162
+ }
163
+
164
+ categories.each do |category, comps|
165
+ available = comps & COMPONENT_FILES.keys
166
+ next if available.empty?
167
+
168
+ say " #{category}:", :yellow
169
+ available.each do |name|
170
+ controller_info = COMPONENT_FILES[name][:controller] ? " ✦" : ""
171
+ say " #{name}#{controller_info}"
172
+ end
173
+ say ""
174
+ end
175
+
176
+ say " ✦ = includes Stimulus controller", :cyan
177
+ say ""
178
+ say "Usage:", :yellow
179
+ say " rails generate shadcn:add button"
180
+ say " rails generate shadcn:add button card dialog"
181
+ say " rails generate shadcn:add --all"
182
+ say ""
183
+ end
184
+
185
+ def validate_requested_components
186
+ if components.empty?
187
+ say "Error: Please specify at least one component or use --all", :red
188
+ say ""
189
+ display_available_components
190
+ exit 1
191
+ end
192
+
193
+ invalid = components - COMPONENT_FILES.keys
194
+ if invalid.any?
195
+ say "Error: Unknown component(s): #{invalid.join(', ')}", :red
196
+ say ""
197
+ display_available_components
198
+ exit 1
199
+ end
200
+ end
201
+
202
+ def add_component(name)
203
+ files = COMPONENT_FILES[name]
204
+
205
+ # Copy Ruby component
206
+ copy_ruby_component(name, files[:component])
207
+
208
+ # Copy ERB template if it exists
209
+ copy_erb_template(name, files[:component])
210
+
211
+ # Copy Stimulus controller if requested and exists
212
+ if include_controllers? && files[:controller]
213
+ copy_stimulus_controller(name, files[:controller])
214
+ end
215
+ end
216
+
217
+ def copy_ruby_component(name, filename)
218
+ source_path = gem_component_path(filename)
219
+ destination_path = File.join(options[:path], "shadcn", filename)
220
+
221
+ if File.exist?(destination_path) && !options[:force]
222
+ say " skip #{filename} (already exists, use --force to overwrite)", :yellow
223
+ else
224
+ copy_file source_path, destination_path
225
+ say " create #{filename}", :green
226
+ end
227
+ end
228
+
229
+ def copy_erb_template(name, ruby_filename)
230
+ erb_filename = ruby_filename.sub(/\.rb$/, ".html.erb")
231
+ source_path = gem_component_path(erb_filename)
232
+
233
+ # Only copy if the template exists
234
+ return unless File.exist?(source_path)
235
+
236
+ destination_path = File.join(options[:path], "shadcn", erb_filename)
237
+
238
+ if File.exist?(destination_path) && !options[:force]
239
+ say " skip #{erb_filename} (already exists, use --force to overwrite)", :yellow
240
+ else
241
+ copy_file source_path, destination_path
242
+ say " create #{erb_filename}", :green
243
+ end
244
+ end
245
+
246
+ def copy_stimulus_controller(name, filename)
247
+ source_path = gem_controller_path(filename)
248
+ destination_path = "app/javascript/controllers/shadcn/#{filename}"
249
+
250
+ # Create directory if it doesn't exist
251
+ directory = File.dirname(destination_path)
252
+ empty_directory directory unless File.directory?(directory)
253
+
254
+ if File.exist?(destination_path) && !options[:force]
255
+ say " skip #{filename} (already exists, use --force to overwrite)", :yellow
256
+ else
257
+ copy_file source_path, destination_path
258
+ say " create #{filename}", :green
259
+ end
260
+ end
261
+
262
+ def gem_component_path(filename)
263
+ File.join(gem_root, "app/components/shadcn", filename)
264
+ end
265
+
266
+ def gem_controller_path(filename)
267
+ File.join(gem_root, "app/assets/javascripts/shadcn/controllers", filename)
268
+ end
269
+
270
+ def gem_root
271
+ @gem_root ||= File.expand_path("../../../../..", __FILE__)
272
+ end
273
+
274
+ def include_controllers?
275
+ options[:include_controllers] && !options[:exclude_controllers]
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,22 @@
1
+ Description:
2
+ Installs and configures shadcn-rails in your Rails application.
3
+
4
+ Example:
5
+ rails generate shadcn:install
6
+
7
+ This will create:
8
+ config/initializers/shadcn.rb
9
+ config/shadcn.yml
10
+
11
+ And configure:
12
+ Tailwind CSS (if present)
13
+ Stimulus controllers
14
+
15
+ Options:
16
+ --theme=NAME Base color theme (default: neutral)
17
+ Available: neutral, slate, stone, zinc, gray
18
+
19
+ --dark-mode=MODE Dark mode strategy (default: class)
20
+ Available: class, media, both
21
+
22
+ --skip-tailwind Skip Tailwind CSS configuration
@@ -7,7 +7,7 @@ module Shadcn
7
7
  module Generators
8
8
  # Generator for installing shadcn-rails in a Rails application
9
9
  # Usage: rails generate shadcn:install
10
- class InstallGenerator < Rails::Generators::Base
10
+ class InstallGenerator < ::Rails::Generators::Base
11
11
  source_root File.expand_path("templates", __dir__)
12
12
 
13
13
  class_option :theme, type: :string, default: "neutral",
@@ -15,7 +15,7 @@ module Shadcn
15
15
  class_option :css_variables, type: :boolean, default: true,
16
16
  desc: "Use CSS variables for theming"
17
17
  class_option :dark_mode, type: :string, default: "class",
18
- desc: "Dark mode strategy (class, media, selector)"
18
+ desc: "Dark mode strategy (class, media, both)"
19
19
  class_option :skip_tailwind, type: :boolean, default: false,
20
20
  desc: "Skip Tailwind CSS configuration"
21
21
 
@@ -41,7 +41,7 @@ module Shadcn
41
41
  "\n/*\n *= require shadcn/base\n *= require shadcn/components\n */\n"
42
42
  end
43
43
  else
44
- say "Could not find application stylesheet. Please manually import shadcn/base.css and shadcn/components.css", :yellow
44
+ say "Could not find application stylesheet. Please manually import shadcn styles.", :yellow
45
45
  end
46
46
  end
47
47
 
@@ -81,6 +81,11 @@ module Shadcn
81
81
  say " Click me"
82
82
  say " <% end %>"
83
83
  say ""
84
+ say "To add individual components to your app for customization:"
85
+ say ""
86
+ say " rails generate shadcn:add button"
87
+ say " rails generate shadcn:add --list"
88
+ say ""
84
89
  say "For more information, visit: https://github.com/iheanyi/shadcn-rails"
85
90
  say ""
86
91
  end
@@ -2,34 +2,14 @@
2
2
 
3
3
  # shadcn-rails configuration
4
4
  Shadcn::Rails.configure do |config|
5
- # Style preset (currently only "default" is supported)
6
- config.style = "default"
7
-
8
- # Base color theme: neutral, stone, zinc, gray, slate
5
+ # Base color theme
6
+ # Available themes: neutral, slate, stone, zinc, gray
9
7
  config.base_color = "<%= options[:theme] %>"
10
8
 
11
- # Whether to use CSS variables for theming
12
- config.css_variables = <%= options[:css_variables] %>
13
-
14
- # Prefix for Tailwind classes (e.g., "tw-")
15
- # config.tailwind_prefix = ""
16
-
17
- # Default border radius for components
18
- config.radius = "0.5rem"
19
-
20
- # Dark mode strategy: :class, :media, or :selector
9
+ # Dark mode strategy
10
+ # Available strategies: :class, :media, :both
11
+ # - :class - Uses .dark class on <html> for manual toggling
12
+ # - :media - Uses @media (prefers-color-scheme: dark) for system preference
13
+ # - :both - Includes both for maximum flexibility
21
14
  config.dark_mode = :<%= options[:dark_mode] %>
22
-
23
- # Icon library to use: :lucide (default)
24
- # config.icon_library = :lucide
25
-
26
- # Register custom component aliases
27
- # config.alias_component :my_button, MyCustomButtonComponent
28
-
29
- # Register custom themes
30
- # config.register_theme :corporate, {
31
- # primary: "210 100% 50%",
32
- # primary_foreground: "0 0% 100%",
33
- # ...
34
- # }
35
15
  end
@@ -1,35 +1,19 @@
1
- # shadcn-rails configuration file
2
- # This file mirrors the components.json structure from shadcn/ui
1
+ # shadcn-rails configuration
2
+ # This file stores component preferences and customizations
3
3
 
4
- # Style preset (currently only "default" is supported)
5
- style: "default"
4
+ # Base color theme
5
+ # Available: neutral, slate, stone, zinc, gray
6
+ base_color: <%= options[:theme] %>
6
7
 
7
- # Tailwind CSS configuration
8
- tailwind:
9
- # Path to your CSS file (for Tailwind CSS v4, leave config blank)
10
- css: "app/assets/stylesheets/application.tailwind.css"
8
+ # Dark mode strategy
9
+ # Available: class, media, both
10
+ dark_mode: <%= options[:dark_mode] %>
11
11
 
12
- # Base color theme: neutral, stone, zinc, gray, slate
13
- base_color: "<%= options[:theme] %>"
12
+ # Component aliases (optional)
13
+ # Map custom names to shadcn components
14
+ # aliases:
15
+ # btn: button
16
+ # modal: dialog
14
17
 
15
- # Use CSS variables for theming
16
- css_variables: <%= options[:css_variables] %>
17
-
18
- # Prefix for Tailwind utility classes (e.g., "tw-")
19
- prefix: ""
20
-
21
- # Dark mode configuration
22
- dark_mode: "<%= options[:dark_mode] %>"
23
-
24
- # Component aliases for path mapping
25
- aliases:
26
- components: "app/components"
27
- ui: "app/components/shadcn"
28
- lib: "lib"
29
- hooks: "app/javascript/hooks"
30
-
31
- # Custom themes (optional)
32
- # themes:
33
- # corporate:
34
- # primary: "210 100% 50%"
35
- # primary_foreground: "0 0% 100%"
18
+ # CSS variables (enabled by default)
19
+ css_variables: <%= options[:css_variables] %>
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Shadcn
4
4
  module Rails
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.1"
6
6
  end
7
7
  end