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
@@ -1,203 +0,0 @@
1
- import { Application } from "@hotwired/stimulus"
2
-
3
- /**
4
- * Helper to set up Stimulus controller tests
5
- * Creates a DOM element with the controller connected
6
- */
7
- export function setupController(Controller, html, controllerName = 'test') {
8
- const application = Application.start()
9
- application.register(controllerName, Controller)
10
-
11
- document.body.innerHTML = html
12
-
13
- // Wait for Stimulus to connect the controller
14
- return new Promise((resolve) => {
15
- requestAnimationFrame(() => {
16
- const element = document.querySelector(`[data-controller="${controllerName}"]`)
17
- const controller = application.getControllerForElementAndIdentifier(element, controllerName)
18
- resolve({ application, element, controller })
19
- })
20
- })
21
- }
22
-
23
- /**
24
- * Clean up after tests
25
- */
26
- export function cleanupController(application) {
27
- if (application) {
28
- application.stop()
29
- }
30
- document.body.innerHTML = ''
31
- }
32
-
33
- /**
34
- * Simulate a click event on an element
35
- */
36
- export function click(element) {
37
- element.dispatchEvent(new MouseEvent('click', {
38
- bubbles: true,
39
- cancelable: true,
40
- view: window
41
- }))
42
- }
43
-
44
- /**
45
- * Wait for a specified number of milliseconds
46
- */
47
- export function wait(ms) {
48
- return new Promise(resolve => setTimeout(resolve, ms))
49
- }
50
-
51
- /**
52
- * Wait for an animation frame
53
- */
54
- export function nextFrame() {
55
- return new Promise(resolve => requestAnimationFrame(resolve))
56
- }
57
-
58
- /**
59
- * Simulate a keyboard event on an element
60
- * @param {Element} element - Target element
61
- * @param {string} key - Key name (e.g., 'ArrowDown', 'Enter', 'Escape')
62
- * @param {Object} options - Additional options (shiftKey, ctrlKey, etc.)
63
- */
64
- export function keydown(element, key, options = {}) {
65
- element.dispatchEvent(new KeyboardEvent('keydown', {
66
- key,
67
- bubbles: true,
68
- cancelable: true,
69
- ...options
70
- }))
71
- }
72
-
73
- /**
74
- * Simulate a keyup event
75
- */
76
- export function keyup(element, key, options = {}) {
77
- element.dispatchEvent(new KeyboardEvent('keyup', {
78
- key,
79
- bubbles: true,
80
- cancelable: true,
81
- ...options
82
- }))
83
- }
84
-
85
- /**
86
- * Wait for a portal element to appear in the DOM
87
- * @param {string} selector - CSS selector for the portal
88
- * @param {boolean} shouldExist - Whether portal should exist (true) or not exist (false)
89
- * @param {number} timeout - Maximum wait time in ms
90
- */
91
- export async function waitForPortal(selector, shouldExist = true, timeout = 1000) {
92
- const startTime = Date.now()
93
-
94
- while (Date.now() - startTime < timeout) {
95
- const element = document.querySelector(selector)
96
- if (shouldExist && element) return element
97
- if (!shouldExist && !element) return null
98
- await wait(10)
99
- }
100
-
101
- throw new Error(`Portal ${selector} ${shouldExist ? 'did not appear' : 'did not disappear'} within ${timeout}ms`)
102
- }
103
-
104
- /**
105
- * Mock window.location for URL-related tests
106
- * Uses jsdom's internal reconfigure or modifies location properties
107
- * @param {string} url - The URL to mock
108
- * @returns {Function} Cleanup function to restore original location
109
- */
110
- export function mockLocation(url) {
111
- // Store original href
112
- const originalHref = window.location.href
113
-
114
- // Use jsdom's reconfigure if available (available in newer jsdom)
115
- if (typeof window._virtualConsole !== 'undefined' && window.location._setHref) {
116
- window.location._setHref(url)
117
- return () => {
118
- if (window.location._setHref) {
119
- window.location._setHref(originalHref)
120
- }
121
- }
122
- }
123
-
124
- // Fallback: Use history API to change URL without navigation
125
- const urlObj = new URL(url, window.location.origin)
126
- window.history.replaceState({}, '', urlObj.href)
127
-
128
- return () => {
129
- window.history.replaceState({}, '', originalHref)
130
- }
131
- }
132
-
133
- /**
134
- * Mock history.pushState and replaceState for URL sync tests
135
- */
136
- export function mockHistory() {
137
- const originalPushState = window.history.pushState
138
- const originalReplaceState = window.history.replaceState
139
-
140
- const calls = {
141
- pushState: [],
142
- replaceState: []
143
- }
144
-
145
- window.history.pushState = (state, title, url) => {
146
- calls.pushState.push({ state, title, url })
147
- }
148
-
149
- window.history.replaceState = (state, title, url) => {
150
- calls.replaceState.push({ state, title, url })
151
- }
152
-
153
- return {
154
- calls,
155
- restore: () => {
156
- window.history.pushState = originalPushState
157
- window.history.replaceState = originalReplaceState
158
- }
159
- }
160
- }
161
-
162
- /**
163
- * Get all focusable elements within a container
164
- * @param {Element} container - Container element
165
- */
166
- export function getFocusableElements(container) {
167
- return container.querySelectorAll(
168
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
169
- )
170
- }
171
-
172
- /**
173
- * Dispatch a custom event
174
- * @param {Element} element - Target element
175
- * @param {string} eventName - Event name
176
- * @param {Object} detail - Event detail object
177
- */
178
- export function dispatchEvent(element, eventName, detail = {}) {
179
- element.dispatchEvent(new CustomEvent(eventName, {
180
- bubbles: true,
181
- cancelable: true,
182
- detail
183
- }))
184
- }
185
-
186
- /**
187
- * Wait for controller to emit a specific event
188
- * @param {Element} element - Element to listen on
189
- * @param {string} eventName - Event name (e.g., 'shadcn--accordion:expand')
190
- * @param {number} timeout - Maximum wait time
191
- */
192
- export function waitForEvent(element, eventName, timeout = 1000) {
193
- return new Promise((resolve, reject) => {
194
- const timer = setTimeout(() => {
195
- reject(new Error(`Event ${eventName} not received within ${timeout}ms`))
196
- }, timeout)
197
-
198
- element.addEventListener(eventName, (event) => {
199
- clearTimeout(timer)
200
- resolve(event)
201
- }, { once: true })
202
- })
203
- }
data/babel.config.cjs DELETED
@@ -1,5 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- ['@babel/preset-env', { targets: { node: 'current' } }]
4
- ]
5
- }
data/bin/console DELETED
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "shadcn/rails"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- require "irb"
11
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/jest.config.js DELETED
@@ -1,19 +0,0 @@
1
- /** @type {import('jest').Config} */
2
- export default {
3
- testEnvironment: 'jsdom',
4
- testMatch: ['**/__tests__/**/*.test.js'],
5
- moduleFileExtensions: ['js', 'json'],
6
- transform: {
7
- '^.+\\.js$': 'babel-jest'
8
- },
9
- transformIgnorePatterns: [
10
- '/node_modules/(?!@hotwired/stimulus)'
11
- ],
12
- setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
13
- collectCoverageFrom: [
14
- 'app/assets/javascripts/shadcn/controllers/**/*.js',
15
- '!**/node_modules/**'
16
- ],
17
- coverageDirectory: 'coverage',
18
- verbose: true
19
- }
data/jest.setup.js DELETED
@@ -1,8 +0,0 @@
1
- import '@testing-library/jest-dom'
2
-
3
- // Make jest globals available in ESM modules
4
- import { jest } from '@jest/globals'
5
- globalThis.jest = jest
6
-
7
- // Mock scrollIntoView since JSDOM doesn't implement it
8
- Element.prototype.scrollIntoView = jest.fn()
@@ -1,188 +0,0 @@
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 individual shadcn components
9
- # Usage: rails generate shadcn:component button card dialog
10
- class ComponentGenerator < Rails::Generators::Base
11
- source_root File.expand_path("templates", __dir__)
12
-
13
- argument :components, type: :array, default: [], banner: "component [component ...]"
14
-
15
- class_option :all, type: :boolean, default: false,
16
- desc: "Install all available components"
17
- class_option :list, type: :boolean, default: false,
18
- desc: "List all available components"
19
-
20
- desc "Adds shadcn-rails components to your application"
21
-
22
- # List of all available components
23
- AVAILABLE_COMPONENTS = %w[
24
- accordion
25
- alert
26
- avatar
27
- badge
28
- button
29
- card
30
- checkbox
31
- collapsible
32
- dialog
33
- dropdown_menu
34
- input
35
- label
36
- popover
37
- progress
38
- scroll_area
39
- select
40
- separator
41
- sheet
42
- skeleton
43
- switch
44
- table
45
- tabs
46
- textarea
47
- toast
48
- tooltip
49
- ].freeze
50
-
51
- def validate_components
52
- if options[:list]
53
- display_available_components
54
- exit 0
55
- end
56
-
57
- if options[:all]
58
- @components_to_install = AVAILABLE_COMPONENTS
59
- else
60
- validate_requested_components
61
- @components_to_install = components
62
- end
63
- end
64
-
65
- def display_installation_plan
66
- say ""
67
- say "Installing #{@components_to_install.length} component(s):", :green
68
- @components_to_install.each { |c| say " - #{c}" }
69
- say ""
70
- end
71
-
72
- def install_components
73
- @components_to_install.each do |component|
74
- install_component(component)
75
- end
76
- end
77
-
78
- def display_usage_examples
79
- say ""
80
- say "=" * 60, :green
81
- say " Components installed successfully!", :green
82
- say "=" * 60, :green
83
- say ""
84
- say "Example usage:", :yellow
85
-
86
- @components_to_install.first(3).each do |component|
87
- display_component_example(component)
88
- end
89
-
90
- say ""
91
- end
92
-
93
- private
94
-
95
- def display_available_components
96
- say ""
97
- say "Available shadcn-rails components:", :green
98
- say ""
99
- AVAILABLE_COMPONENTS.each { |c| say " - #{c}" }
100
- say ""
101
- say "Usage: rails generate shadcn:component button card dialog"
102
- say " rails generate shadcn:component --all"
103
- say ""
104
- end
105
-
106
- def validate_requested_components
107
- if components.empty?
108
- say "Error: Please specify at least one component or use --all", :red
109
- say ""
110
- display_available_components
111
- exit 1
112
- end
113
-
114
- invalid = components - AVAILABLE_COMPONENTS
115
- if invalid.any?
116
- say "Error: Unknown component(s): #{invalid.join(', ')}", :red
117
- say ""
118
- display_available_components
119
- exit 1
120
- end
121
- end
122
-
123
- def install_component(name)
124
- # Components are already available from the gem
125
- # This generator just confirms installation and provides usage info
126
- say " ✓ #{name.titleize}Component is available", :green
127
- end
128
-
129
- def display_component_example(component)
130
- case component
131
- when "button"
132
- say <<~EXAMPLE
133
-
134
- Button:
135
- <%= render Shadcn::ButtonComponent.new(variant: :primary) do %>
136
- Click me
137
- <% end %>
138
- EXAMPLE
139
- when "card"
140
- say <<~EXAMPLE
141
-
142
- Card:
143
- <%= render Shadcn::CardComponent.new do |card| %>
144
- <% card.with_header do %>
145
- <% card.with_title { "Card Title" } %>
146
- <% end %>
147
- <% card.with_content { "Card content" } %>
148
- <% end %>
149
- EXAMPLE
150
- when "dialog"
151
- say <<~EXAMPLE
152
-
153
- Dialog:
154
- <%= render Shadcn::DialogComponent.new do |dialog| %>
155
- <% dialog.with_trigger do %>
156
- <%= render Shadcn::ButtonComponent.new { "Open" } %>
157
- <% end %>
158
- <% dialog.with_content do |content| %>
159
- <% content.with_title { "Dialog Title" } %>
160
- Dialog content here
161
- <% end %>
162
- <% end %>
163
- EXAMPLE
164
- when "input"
165
- say <<~EXAMPLE
166
-
167
- Input:
168
- <%= render Shadcn::LabelComponent.new(for: "email") { "Email" } %>
169
- <%= render Shadcn::InputComponent.new(
170
- type: "email",
171
- id: "email",
172
- name: "email",
173
- placeholder: "Enter your email"
174
- ) %>
175
- EXAMPLE
176
- else
177
- say <<~EXAMPLE
178
-
179
- #{component.titleize}:
180
- <%= render Shadcn::#{component.camelize}Component.new do %>
181
- Content here
182
- <% end %>
183
- EXAMPLE
184
- end
185
- end
186
- end
187
- end
188
- end
@@ -1,128 +0,0 @@
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 switching or customizing themes
9
- # Usage: rails generate shadcn:theme slate
10
- class ThemeGenerator < Rails::Generators::Base
11
- source_root File.expand_path("templates", __dir__)
12
-
13
- argument :theme_name, type: :string, default: "neutral",
14
- banner: "theme_name"
15
-
16
- class_option :list, type: :boolean, default: false,
17
- desc: "List all available themes"
18
-
19
- desc "Changes the shadcn-rails theme or creates a custom theme"
20
-
21
- AVAILABLE_THEMES = %w[neutral slate stone zinc gray].freeze
22
-
23
- def validate_theme
24
- if options[:list]
25
- display_available_themes
26
- exit 0
27
- end
28
-
29
- unless AVAILABLE_THEMES.include?(theme_name)
30
- say "Error: Unknown theme '#{theme_name}'", :red
31
- display_available_themes
32
- exit 1
33
- end
34
- end
35
-
36
- def update_initializer
37
- initializer_path = "config/initializers/shadcn.rb"
38
-
39
- if File.exist?(initializer_path)
40
- gsub_file initializer_path,
41
- /config\.base_color = ["']?\w+["']?/,
42
- "config.base_color = \"#{theme_name}\""
43
- say "Updated initializer to use '#{theme_name}' theme", :green
44
- else
45
- say "Initializer not found. Run 'rails generate shadcn:install' first.", :yellow
46
- end
47
- end
48
-
49
- def update_config_file
50
- config_path = "config/shadcn.yml"
51
-
52
- if File.exist?(config_path)
53
- gsub_file config_path,
54
- /base_color: \w+/,
55
- "base_color: #{theme_name}"
56
- say "Updated shadcn.yml to use '#{theme_name}' theme", :green
57
- end
58
- end
59
-
60
- def update_stylesheet
61
- # Check for theme import in stylesheet
62
- stylesheet_path = "app/assets/stylesheets/application.tailwind.css"
63
-
64
- if File.exist?(stylesheet_path)
65
- content = File.read(stylesheet_path)
66
-
67
- # Remove existing theme import
68
- AVAILABLE_THEMES.each do |theme|
69
- next if theme == "neutral" # neutral uses base.css
70
- gsub_file stylesheet_path, /@import ["']shadcn\/themes\/#{theme}["'];\n?/, ""
71
- end
72
-
73
- # Add new theme import if not neutral
74
- unless theme_name == "neutral"
75
- inject_into_file stylesheet_path, after: '@import "shadcn/base";' do
76
- "\n@import \"shadcn/themes/#{theme_name}\";"
77
- end
78
- end
79
-
80
- say "Updated stylesheet with '#{theme_name}' theme", :green
81
- end
82
- end
83
-
84
- def display_completion_message
85
- say ""
86
- say "Theme changed to '#{theme_name}'!", :green
87
- say ""
88
- say "The following colors are now active:", :yellow
89
- display_theme_preview(theme_name)
90
- say ""
91
- end
92
-
93
- private
94
-
95
- def display_available_themes
96
- say ""
97
- say "Available themes:", :green
98
- say ""
99
- AVAILABLE_THEMES.each do |theme|
100
- say " - #{theme}"
101
- end
102
- say ""
103
- say "Usage: rails generate shadcn:theme slate"
104
- say ""
105
- end
106
-
107
- def display_theme_preview(theme)
108
- case theme
109
- when "neutral"
110
- say " Primary: Pure black/white"
111
- say " Style: Clean, minimal"
112
- when "slate"
113
- say " Primary: Cool blue-gray"
114
- say " Style: Professional, corporate"
115
- when "stone"
116
- say " Primary: Warm gray-brown"
117
- say " Style: Earthy, natural"
118
- when "zinc"
119
- say " Primary: Cool gray"
120
- say " Style: Modern, sleek"
121
- when "gray"
122
- say " Primary: True gray"
123
- say " Style: Neutral, balanced"
124
- end
125
- end
126
- end
127
- end
128
- end